Merge pull request 'Release v1.2.7 - 列表样式优化' (#2) from feature/v1.2.7 into main
Reviewed-on: http://47.253.189.47:3000/coolbot/jiachenlong/pulls/2
This commit is contained in:
commit
51834f4498
|
|
@ -55,6 +55,7 @@ class Collection(Base):
|
||||||
f02_11_version = Column(String(100), nullable=True, index=True)
|
f02_11_version = Column(String(100), nullable=True, index=True)
|
||||||
f02_12_packaging = Column(String(100), nullable=True, index=True)
|
f02_12_packaging = Column(String(100), nullable=True, index=True)
|
||||||
f02_13_rarity = Column(String(50), nullable=True, index=True) # 珍惜度
|
f02_13_rarity = Column(String(50), nullable=True, index=True) # 珍惜度
|
||||||
|
f02_14_number_category = Column(String(20), nullable=True, index=True) # 号码分类
|
||||||
|
|
||||||
# f03 评级信息
|
# f03 评级信息
|
||||||
f03_20_is_graded = Column(Boolean, default=False, index=True)
|
f03_20_is_graded = Column(Boolean, default=False, index=True)
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ def to_camel_case(data: dict) -> dict:
|
||||||
'f02_11_version': 'version',
|
'f02_11_version': 'version',
|
||||||
'f02_12_packaging': 'packaging',
|
'f02_12_packaging': 'packaging',
|
||||||
'f02_13_rarity': 'rarity',
|
'f02_13_rarity': 'rarity',
|
||||||
|
'f02_14_number_category': 'numberCategory',
|
||||||
'f03_20_is_graded': 'isGraded',
|
'f03_20_is_graded': 'isGraded',
|
||||||
'f03_21_grading_company': 'gradingCompany',
|
'f03_21_grading_company': 'gradingCompany',
|
||||||
'f03_22_grading_score': 'gradingScore',
|
'f03_22_grading_score': 'gradingScore',
|
||||||
|
|
@ -73,8 +74,8 @@ def generate_code(version: str, user_id: str, db: Session) -> str:
|
||||||
|
|
||||||
max_num = 0
|
max_num = 0
|
||||||
for (code,) in user_codes:
|
for (code,) in user_codes:
|
||||||
# 只处理纯数字或 4 位数字编码(忽略 TEST001 等特殊编码)
|
# 处理纯数字编码(支持4位和5位)
|
||||||
if re.match(r'^\d{4}$', code):
|
if re.match(r'^\d{4,5}$', code):
|
||||||
try:
|
try:
|
||||||
num = int(code)
|
num = int(code)
|
||||||
if num > max_num:
|
if num > max_num:
|
||||||
|
|
@ -84,7 +85,12 @@ def generate_code(version: str, user_id: str, db: Session) -> str:
|
||||||
|
|
||||||
# 当前用户最大号 +1
|
# 当前用户最大号 +1
|
||||||
next_num = max_num + 1
|
next_num = max_num + 1
|
||||||
return str(next_num).zfill(4)
|
|
||||||
|
# 如果超过9999,使用5位;否则使用4位
|
||||||
|
if next_num > 9999:
|
||||||
|
return str(next_num).zfill(5)
|
||||||
|
else:
|
||||||
|
return str(next_num).zfill(4)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/next-code")
|
@router.get("/next-code")
|
||||||
|
|
@ -164,6 +170,7 @@ def get_collections(
|
||||||
'f02_11_version': collection_item.f02_11_version,
|
'f02_11_version': collection_item.f02_11_version,
|
||||||
'f02_12_packaging': collection_item.f02_12_packaging,
|
'f02_12_packaging': collection_item.f02_12_packaging,
|
||||||
'f02_13_rarity': collection_item.f02_13_rarity,
|
'f02_13_rarity': collection_item.f02_13_rarity,
|
||||||
|
'f02_14_number_category': collection_item.f02_14_number_category,
|
||||||
'f03_20_is_graded': collection_item.f03_20_is_graded,
|
'f03_20_is_graded': collection_item.f03_20_is_graded,
|
||||||
'f03_21_grading_company': collection_item.f03_21_grading_company,
|
'f03_21_grading_company': collection_item.f03_21_grading_company,
|
||||||
'f03_22_grading_score': collection_item.f03_22_grading_score,
|
'f03_22_grading_score': collection_item.f03_22_grading_score,
|
||||||
|
|
@ -247,6 +254,7 @@ def get_stats(
|
||||||
by_grading_company = Counter(c.f03_21_grading_company for c in all_collections if c.f03_20_is_graded and c.f03_21_grading_company).items()
|
by_grading_company = Counter(c.f03_21_grading_company for c in all_collections if c.f03_20_is_graded and c.f03_21_grading_company).items()
|
||||||
by_grading_score = Counter(c.f03_22_grading_score for c in all_collections if c.f03_20_is_graded and c.f03_22_grading_score).items()
|
by_grading_score = Counter(c.f03_22_grading_score for c in all_collections if c.f03_20_is_graded and c.f03_22_grading_score).items()
|
||||||
by_special_mark = Counter(c.f04_30_special_mark for c in all_collections if c.f04_30_special_mark).items()
|
by_special_mark = Counter(c.f04_30_special_mark for c in all_collections if c.f04_30_special_mark).items()
|
||||||
|
by_number_category = Counter(c.f02_14_number_category for c in all_collections if c.f02_14_number_category).items()
|
||||||
|
|
||||||
# 总成本: SUM(cost_price + repair_fee + grading_fee) for ALL collections
|
# 总成本: SUM(cost_price + repair_fee + grading_fee) for ALL collections
|
||||||
total_cost = sum(
|
total_cost = sum(
|
||||||
|
|
@ -291,6 +299,7 @@ def get_stats(
|
||||||
"byGradingCompany": [{"company": c, "count": n} for c, n in by_grading_company],
|
"byGradingCompany": [{"company": c, "count": n} for c, n in by_grading_company],
|
||||||
"byGradingScore": [{"score": s, "count": n} for s, n in by_grading_score],
|
"byGradingScore": [{"score": s, "count": n} for s, n in by_grading_score],
|
||||||
"bySpecialMark": [{"mark": m, "count": n} for m, n in by_special_mark],
|
"bySpecialMark": [{"mark": m, "count": n} for m, n in by_special_mark],
|
||||||
|
"byNumberCategory": [{"numberCategory": n, "count": c} for n, c in by_number_category],
|
||||||
# 盈亏统计(只统计已售且有价格的藏品)
|
# 盈亏统计(只统计已售且有价格的藏品)
|
||||||
"byProfitLoss": [
|
"byProfitLoss": [
|
||||||
{"type": "profit", "label": "盈利", "count": sum(1 for c in sold_collections if c.f05_42_goal_price and c.f05_40_cost_price and c.f05_42_goal_price > c.f05_40_cost_price)},
|
{"type": "profit", "label": "盈利", "count": sum(1 for c in sold_collections if c.f05_42_goal_price and c.f05_40_cost_price and c.f05_42_goal_price > c.f05_40_cost_price)},
|
||||||
|
|
@ -337,6 +346,7 @@ def get_collection(
|
||||||
'f02_11_version': collection.get('f02_11_version'),
|
'f02_11_version': collection.get('f02_11_version'),
|
||||||
'f02_12_packaging': collection.get('f02_12_packaging'),
|
'f02_12_packaging': collection.get('f02_12_packaging'),
|
||||||
'f02_13_rarity': collection.get('f02_13_rarity'),
|
'f02_13_rarity': collection.get('f02_13_rarity'),
|
||||||
|
'f02_14_number_category': collection.get('f02_14_number_category'),
|
||||||
'f03_20_is_graded': collection.get('f03_20_is_graded'),
|
'f03_20_is_graded': collection.get('f03_20_is_graded'),
|
||||||
'f03_21_grading_company': collection.get('f03_21_grading_company'),
|
'f03_21_grading_company': collection.get('f03_21_grading_company'),
|
||||||
'f03_22_grading_score': collection.get('f03_22_grading_score'),
|
'f03_22_grading_score': collection.get('f03_22_grading_score'),
|
||||||
|
|
@ -446,6 +456,7 @@ def create_collection(
|
||||||
f02_11_version=collection_data.f02_11_version,
|
f02_11_version=collection_data.f02_11_version,
|
||||||
f02_12_packaging=collection_data.f02_12_packaging,
|
f02_12_packaging=collection_data.f02_12_packaging,
|
||||||
f02_13_rarity=collection_data.f02_13_rarity,
|
f02_13_rarity=collection_data.f02_13_rarity,
|
||||||
|
f02_14_number_category=collection_data.f02_14_number_category,
|
||||||
f03_20_is_graded=collection_data.f03_20_is_graded or False,
|
f03_20_is_graded=collection_data.f03_20_is_graded or False,
|
||||||
f03_21_grading_company=collection_data.f03_21_grading_company,
|
f03_21_grading_company=collection_data.f03_21_grading_company,
|
||||||
f03_22_grading_score=collection_data.f03_22_grading_score,
|
f03_22_grading_score=collection_data.f03_22_grading_score,
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ class CollectionBase(BaseModel):
|
||||||
f02_11_version: Optional[str] = Field(None, alias="version")
|
f02_11_version: Optional[str] = Field(None, alias="version")
|
||||||
f02_12_packaging: Optional[str] = Field(None, alias="packaging")
|
f02_12_packaging: Optional[str] = Field(None, alias="packaging")
|
||||||
f02_13_rarity: Optional[str] = Field(None, alias="rarity")
|
f02_13_rarity: Optional[str] = Field(None, alias="rarity")
|
||||||
|
f02_14_number_category: Optional[str] = Field(None, alias="numberCategory")
|
||||||
|
|
||||||
# f03 评级信息
|
# f03 评级信息
|
||||||
f03_20_is_graded: Optional[bool] = Field(False, alias="isGraded")
|
f03_20_is_graded: Optional[bool] = Field(False, alias="isGraded")
|
||||||
|
|
@ -104,6 +105,7 @@ class CollectionUpdate(BaseModel):
|
||||||
f02_11_version: Optional[str] = Field(None, alias="version")
|
f02_11_version: Optional[str] = Field(None, alias="version")
|
||||||
f02_12_packaging: Optional[str] = Field(None, alias="packaging")
|
f02_12_packaging: Optional[str] = Field(None, alias="packaging")
|
||||||
f02_13_rarity: Optional[str] = Field(None, alias="rarity")
|
f02_13_rarity: Optional[str] = Field(None, alias="rarity")
|
||||||
|
f02_14_number_category: Optional[str] = Field(None, alias="numberCategory")
|
||||||
|
|
||||||
# f03 评级信息
|
# f03 评级信息
|
||||||
f03_20_is_graded: Optional[bool] = Field(None, alias="isGraded")
|
f03_20_is_graded: Optional[bool] = Field(None, alias="isGraded")
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
VERSION=1.2.5
|
VERSION=1.2.6
|
||||||
|
|
|
||||||
|
|
@ -58,3 +58,15 @@ curl -X POST http://localhost:3000/api/auth/send-verification-code \
|
||||||
| 验证码失败 | 检查SMS_*配置是否完整 |
|
| 验证码失败 | 检查SMS_*配置是否完整 |
|
||||||
| 导入错误 | 使用python3.11启动 |
|
| 导入错误 | 使用python3.11启动 |
|
||||||
| 404错误 | 重启后端服务 |
|
| 404错误 | 重启后端服务 |
|
||||||
|
|
||||||
|
### 7. 用户协议页面检查
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
确认返回HTML内容
|
||||||
|
|
||||||
|
### 8. 前端静态文件检查
|
||||||
|
|
||||||
|
确认用户协议文件存在
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
<title>甲辰收藏 v1.2.5</title>
|
<title>甲辰收藏 v1.2.6</title>
|
||||||
|
|
||||||
<!-- Favicon -->
|
<!-- Favicon -->
|
||||||
<link rel="icon" type="image/x-icon" href="/static/icons/favicon.ico" />
|
<link rel="icon" type="image/x-icon" href="/static/icons/favicon.ico" />
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// 添加藏品页面 - 支持 AI 识别/手工录入/批量录入
|
// 添加藏品页面 - 支持 AI 识别/手工录入/批量录入
|
||||||
import React, { useState, useRef } from 'react'
|
import React, { useState, useRef } from 'react'
|
||||||
import { APP_VERSION } from '../config/version'
|
import { APP_VERSION } from '../config/version'
|
||||||
|
const numberCategoryOptions = [{value:'无2347',label:'无2347'},{value:'无347',label:'无347'},{value:'无247',label:'无247'},{value:'无47',label:'无47'},{value:'无4',label:'无4'},{value:'带4',label:'带4'},{value:'其他',label:'其他'}];
|
||||||
|
|
||||||
// 字段转换函数
|
// 字段转换函数
|
||||||
const convertField = (obj) => {
|
const convertField = (obj) => {
|
||||||
|
|
@ -12,7 +13,7 @@ const convertField = (obj) => {
|
||||||
packaging: 'f02_12_packaging', rarity: 'f02_13_rarity',
|
packaging: 'f02_12_packaging', rarity: 'f02_13_rarity',
|
||||||
isGraded: 'f03_20_is_graded', gradingCompany: 'f03_21_grading_company',
|
isGraded: 'f03_20_is_graded', gradingCompany: 'f03_21_grading_company',
|
||||||
gradingScore: 'f03_22_grading_score', threeStar: 'f03_23_three_star',
|
gradingScore: 'f03_22_grading_score', threeStar: 'f03_23_three_star',
|
||||||
specialMark: 'f04_30_special_mark', serialFeature: 'f04_31_serial_feature',
|
specialMark: 'f04_30_special_mark', serialFeature: 'f04_31_serial_feature', numberCategory: 'f02_14_number_category',
|
||||||
issuer: 'f04_32_issuer', issueYear: 'f04_33_issue_year',
|
issuer: 'f04_32_issuer', issueYear: 'f04_33_issue_year',
|
||||||
material: 'f04_34_material', denomination: 'f04_35_denomination',
|
material: 'f04_34_material', denomination: 'f04_35_denomination',
|
||||||
issueQuantity: 'f04_36_issue_quantity',
|
issueQuantity: 'f04_36_issue_quantity',
|
||||||
|
|
@ -61,11 +62,11 @@ const Input = ({ form, handleChange, label, field, type = 'text', options = null
|
||||||
}
|
}
|
||||||
|
|
||||||
const getDefaultForm = () => ({
|
const getDefaultForm = () => ({
|
||||||
name: '龙钞', code: '', category: '自持', rarity: '通货', prefixSerial: '',
|
name: '藏品', code: '', category: '自持', rarity: '通货', prefixSerial: '',
|
||||||
version: '2024 龙', denomination: '', status: 'in_collection', packaging: '单张',
|
version: '2024 龙', denomination: '', status: 'in_collection', packaging: '单张',
|
||||||
material: '塑料钞', issueQuantity: '1 亿', purpose: '收藏', isGraded: false,
|
material: '塑料钞', issueQuantity: '1 亿', purpose: '收藏', isGraded: false,
|
||||||
gradingCompany: '', gradingScore: '', threeStar: false, specialMark: '',
|
gradingCompany: '', gradingScore: '', threeStar: false, specialMark: '',
|
||||||
serialFeature: '', issuer: '中国人民银行', issueYear: '2024',
|
serialFeature: '', numberCategory: '', issuer: '中国人民银行', issueYear: '2024',
|
||||||
costPrice: '', targetPrice: '', goalPrice: '', repairFee: '', gradingFee: '', remark: ''
|
costPrice: '', targetPrice: '', goalPrice: '', repairFee: '', gradingFee: '', remark: ''
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -146,6 +147,23 @@ export default function Add() {
|
||||||
if (key === 'goalPrice' && value) {
|
if (key === 'goalPrice' && value) {
|
||||||
setForm(prev => ({ ...prev, status: 'sold' }))
|
setForm(prev => ({ ...prev, status: 'sold' }))
|
||||||
}
|
}
|
||||||
|
// 冠字号变化时自动分类
|
||||||
|
if (key === 'prefixSerial') {
|
||||||
|
const serial = value
|
||||||
|
let cat = ''
|
||||||
|
const match = serial.match(/J(\d{9})/)
|
||||||
|
const digits = match ? match[1] : serial.replace(/\D/g, '').slice(0, 9)
|
||||||
|
if (digits) {
|
||||||
|
if (!digits.includes('2') && !digits.includes('3') && !digits.includes('4') && !digits.includes('7')) cat = '无2347'
|
||||||
|
else if (!digits.includes('3') && !digits.includes('4') && !digits.includes('7')) cat = '无347'
|
||||||
|
else if (!digits.includes('2') && !digits.includes('4') && !digits.includes('7')) cat = '无247'
|
||||||
|
else if (!digits.includes('4') && !digits.includes('7') && digits.includes('2') && digits.includes('3')) cat = '无47'
|
||||||
|
else if (digits.includes('7') && !digits.includes('4')) cat = '无4'
|
||||||
|
else if (digits.includes('4')) cat = '带4'
|
||||||
|
else cat = '其他'
|
||||||
|
}
|
||||||
|
setForm(prev => ({ ...prev, numberCategory: cat }))
|
||||||
|
}
|
||||||
// 版别变化时同步发行年份
|
// 版别变化时同步发行年份
|
||||||
if (key === 'version') {
|
if (key === 'version') {
|
||||||
const yearMatch = value.match(/(20\d{2})/)
|
const yearMatch = value.match(/(20\d{2})/)
|
||||||
|
|
@ -244,7 +262,26 @@ export default function Add() {
|
||||||
setSaving(true)
|
setSaving(true)
|
||||||
setError('')
|
setError('')
|
||||||
const token = localStorage.getItem('token')
|
const token = localStorage.getItem('token')
|
||||||
const formData = convertField(form)
|
// 保存前自动根据冠字号设置号码分类
|
||||||
|
const formWithCategory = { ...form }
|
||||||
|
if (formWithCategory.prefixSerial && !formWithCategory.numberCategory) {
|
||||||
|
const serial = formWithCategory.prefixSerial
|
||||||
|
const match = serial.match(/J(\d{9})/)
|
||||||
|
const digits = match ? match[1] : serial.replace(/\D/g, '').slice(0, 9)
|
||||||
|
if (digits) {
|
||||||
|
let cat = ''
|
||||||
|
if (!digits.includes('2') && !digits.includes('3') && !digits.includes('4') && !digits.includes('7')) cat = '无2347'
|
||||||
|
else if (!digits.includes('3') && !digits.includes('4') && !digits.includes('7')) cat = '无347'
|
||||||
|
else if (!digits.includes('2') && !digits.includes('4') && !digits.includes('7')) cat = '无247'
|
||||||
|
else if (!digits.includes('4') && !digits.includes('7') && digits.includes('2') && digits.includes('3')) cat = '无47'
|
||||||
|
else if (digits.includes('7') && !digits.includes('4')) cat = '无4'
|
||||||
|
else if (digits.includes('4')) cat = '带4'
|
||||||
|
else cat = '其他'
|
||||||
|
formWithCategory.numberCategory = cat
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const formData = convertField(formWithCategory)
|
||||||
try {
|
try {
|
||||||
// 1. 保存藏品信息(先检查是否重复)
|
// 1. 保存藏品信息(先检查是否重复)
|
||||||
const res = await fetch('/api/collections', {
|
const res = await fetch('/api/collections', {
|
||||||
|
|
@ -259,6 +296,11 @@ export default function Add() {
|
||||||
const result = await res.json()
|
const result = await res.json()
|
||||||
let collectionId = result.f99_90_id || result.id
|
let collectionId = result.f99_90_id || result.id
|
||||||
|
|
||||||
|
// 检查是否有错误(重复编号等)
|
||||||
|
if (result.error) {
|
||||||
|
throw new Error(result.error.message || '保存失败')
|
||||||
|
}
|
||||||
|
|
||||||
// 检查是否有重复警告
|
// 检查是否有重复警告
|
||||||
if (result.warning && result.warning.code === 'DUPLICATE_SERIAL') {
|
if (result.warning && result.warning.code === 'DUPLICATE_SERIAL') {
|
||||||
const { warning } = result
|
const { warning } = result
|
||||||
|
|
@ -536,6 +578,7 @@ export default function Add() {
|
||||||
<div style={{ color: '#fbbf24', fontSize: '14px', fontWeight: 'bold', marginBottom: '12px' }}>基本信息</div>
|
<div style={{ color: '#fbbf24', fontSize: '14px', fontWeight: 'bold', marginBottom: '12px' }}>基本信息</div>
|
||||||
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '12px' }}>
|
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '12px' }}>
|
||||||
<Input form={form} handleChange={handleChange} label="名称 *" field="name" />
|
<Input form={form} handleChange={handleChange} label="名称 *" field="name" />
|
||||||
|
<Input form={form} handleChange={handleChange} label="编号(可选)" field="code" />
|
||||||
<Input form={form} handleChange={handleChange} label="持仓类型" field="category" options={categoryOptions} />
|
<Input form={form} handleChange={handleChange} label="持仓类型" field="category" options={categoryOptions} />
|
||||||
<Input form={form} handleChange={handleChange} label="珍惜度" field="rarity" options={rarityOptions} />
|
<Input form={form} handleChange={handleChange} label="珍惜度" field="rarity" options={rarityOptions} />
|
||||||
<Input form={form} handleChange={handleChange} label="冠字序号" field="prefixSerial" />
|
<Input form={form} handleChange={handleChange} label="冠字序号" field="prefixSerial" />
|
||||||
|
|
@ -573,6 +616,7 @@ export default function Add() {
|
||||||
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '12px' }}>
|
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '12px' }}>
|
||||||
<Input form={form} handleChange={handleChange} label="特殊标识" field="specialMark" />
|
<Input form={form} handleChange={handleChange} label="特殊标识" field="specialMark" />
|
||||||
<Input form={form} handleChange={handleChange} label="号码特征" field="serialFeature" />
|
<Input form={form} handleChange={handleChange} label="号码特征" field="serialFeature" />
|
||||||
|
<Input form={form} handleChange={handleChange} label="号码分类" field="numberCategory" options={numberCategoryOptions} />
|
||||||
<Input form={form} handleChange={handleChange} label="发行方" field="issuer" />
|
<Input form={form} handleChange={handleChange} label="发行方" field="issuer" />
|
||||||
<Input form={form} handleChange={handleChange} label="发行年份" field="issueYear" />
|
<Input form={form} handleChange={handleChange} label="发行年份" field="issueYear" />
|
||||||
<Input form={form} handleChange={handleChange} label="材质" field="material" />
|
<Input form={form} handleChange={handleChange} label="材质" field="material" />
|
||||||
|
|
|
||||||
|
|
@ -230,6 +230,7 @@ export default function Detail() {
|
||||||
<Section title="特殊信息">
|
<Section title="特殊信息">
|
||||||
<Field label="特殊标识" value={collection.specialMark} />
|
<Field label="特殊标识" value={collection.specialMark} />
|
||||||
<Field label="号码特征" value={collection.serialFeature} />
|
<Field label="号码特征" value={collection.serialFeature} />
|
||||||
|
<Field label="号码分类" value={collection.numberCategory} />
|
||||||
<Field label="发行方" value={collection.issuer} />
|
<Field label="发行方" value={collection.issuer} />
|
||||||
<Field label="发行年份" value={collection.issueYear} />
|
<Field label="发行年份" value={collection.issueYear} />
|
||||||
<Field label="材质" value={collection.material} />
|
<Field label="材质" value={collection.material} />
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ const categoryOptions = [
|
||||||
{ value: '其他', label: '其他' }
|
{ value: '其他', label: '其他' }
|
||||||
]
|
]
|
||||||
|
|
||||||
|
const numberCategoryOptions = [{value:'无2347',label:'无2347'},{value:'无347',label:'无347'},{value:'无247',label:'无247'},{value:'无47',label:'无47'},{value:'无4',label:'无4'},{value:'带4',label:'带4'},{value:'其他',label:'其他'}];
|
||||||
const rarityOptions = [
|
const rarityOptions = [
|
||||||
{ value: '通货', label: '通货' },
|
{ value: '通货', label: '通货' },
|
||||||
{ value: '特色', label: '特色' },
|
{ value: '特色', label: '特色' },
|
||||||
|
|
@ -68,7 +69,7 @@ export default function Edit() {
|
||||||
const editId = params.get('id')
|
const editId = params.get('id')
|
||||||
|
|
||||||
const [form, setForm] = useState({
|
const [form, setForm] = useState({
|
||||||
name: '', code: '', category: '自持', rarity: '通货', prefixSerial: '',
|
name: '', code: '', category: '自持', rarity: '通货', numberCategory: '', prefixSerial: '',
|
||||||
version: '2024 龙', denomination: '', status: 'in_collection', packaging: '单张',
|
version: '2024 龙', denomination: '', status: 'in_collection', packaging: '单张',
|
||||||
material: '塑料钞', issueQuantity: '1 亿', purpose: '收藏', isGraded: false,
|
material: '塑料钞', issueQuantity: '1 亿', purpose: '收藏', isGraded: false,
|
||||||
gradingCompany: '', gradingScore: '', threeStar: false, specialMark: '',
|
gradingCompany: '', gradingScore: '', threeStar: false, specialMark: '',
|
||||||
|
|
@ -96,6 +97,7 @@ export default function Edit() {
|
||||||
code: data.code || '',
|
code: data.code || '',
|
||||||
category: data.category || '自持',
|
category: data.category || '自持',
|
||||||
rarity: data.rarity || '通货',
|
rarity: data.rarity || '通货',
|
||||||
|
numberCategory: data.numberCategory || '',
|
||||||
prefixSerial: data.prefixSerial || '',
|
prefixSerial: data.prefixSerial || '',
|
||||||
version: data.version || '2024 龙',
|
version: data.version || '2024 龙',
|
||||||
denomination: data.denomination || '',
|
denomination: data.denomination || '',
|
||||||
|
|
@ -146,6 +148,23 @@ export default function Edit() {
|
||||||
const handleChange = (key, value) => {
|
const handleChange = (key, value) => {
|
||||||
setForm({ ...form, [key]: value })
|
setForm({ ...form, [key]: value })
|
||||||
if (key === 'targetPrice' && value) setForm(prev => ({ ...prev, status: 'sold' }))
|
if (key === 'targetPrice' && value) setForm(prev => ({ ...prev, status: 'sold' }))
|
||||||
|
// 冠字号变化时自动分类
|
||||||
|
if (key === 'prefixSerial') {
|
||||||
|
const serial = value
|
||||||
|
let cat = ''
|
||||||
|
const match = serial.match(/J(\d{9})/)
|
||||||
|
const digits = match ? match[1] : serial.replace(/\D/g, '').slice(0, 9)
|
||||||
|
if (digits) {
|
||||||
|
if (!digits.includes('2') && !digits.includes('3') && !digits.includes('4') && !digits.includes('7')) cat = '无2347'
|
||||||
|
else if (!digits.includes('3') && !digits.includes('4') && !digits.includes('7')) cat = '无347'
|
||||||
|
else if (digits.includes('4')) cat = '带4'
|
||||||
|
else if (digits.includes('7')) cat = '无4'
|
||||||
|
else if (!digits.includes('4') && !digits.includes('7')) cat = '无47'
|
||||||
|
else if (!digits.includes('2') && !digits.includes('4') && !digits.includes('7')) cat = '无247'
|
||||||
|
else cat = '其他'
|
||||||
|
}
|
||||||
|
setForm(prev => ({ ...prev, numberCategory: cat }))
|
||||||
|
}
|
||||||
// 版别变化时同步发行年份
|
// 版别变化时同步发行年份
|
||||||
if (key === 'version') {
|
if (key === 'version') {
|
||||||
const yearMatch = value.match(/(20\d{2})/)
|
const yearMatch = value.match(/(20\d{2})/)
|
||||||
|
|
@ -238,6 +257,7 @@ export default function Edit() {
|
||||||
status: 'f01_04_status', remark: 'f01_05_remark',
|
status: 'f01_04_status', remark: 'f01_05_remark',
|
||||||
prefixSerial: 'f02_10_prefix_serial', version: 'f02_11_version',
|
prefixSerial: 'f02_10_prefix_serial', version: 'f02_11_version',
|
||||||
packaging: 'f02_12_packaging', rarity: 'f02_13_rarity',
|
packaging: 'f02_12_packaging', rarity: 'f02_13_rarity',
|
||||||
|
numberCategory: 'f02_14_number_category',
|
||||||
isGraded: 'f03_20_is_graded', gradingCompany: 'f03_21_grading_company',
|
isGraded: 'f03_20_is_graded', gradingCompany: 'f03_21_grading_company',
|
||||||
gradingScore: 'f03_22_grading_score', threeStar: 'f03_23_three_star',
|
gradingScore: 'f03_22_grading_score', threeStar: 'f03_23_three_star',
|
||||||
specialMark: 'f04_30_special_mark', serialFeature: 'f04_31_serial_feature',
|
specialMark: 'f04_30_special_mark', serialFeature: 'f04_31_serial_feature',
|
||||||
|
|
@ -398,6 +418,7 @@ export default function Edit() {
|
||||||
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '12px' }}>
|
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '12px' }}>
|
||||||
<Input form={form} handleChange={handleChange} label="特殊标识" field="specialMark" />
|
<Input form={form} handleChange={handleChange} label="特殊标识" field="specialMark" />
|
||||||
<Input form={form} handleChange={handleChange} label="号码特征" field="serialFeature" />
|
<Input form={form} handleChange={handleChange} label="号码特征" field="serialFeature" />
|
||||||
|
<Input form={form} handleChange={handleChange} label="号码分类" field="numberCategory" options={numberCategoryOptions} />
|
||||||
<Input form={form} handleChange={handleChange} label="发行方" field="issuer" />
|
<Input form={form} handleChange={handleChange} label="发行方" field="issuer" />
|
||||||
<Input form={form} handleChange={handleChange} label="发行年份" field="issueYear" />
|
<Input form={form} handleChange={handleChange} label="发行年份" field="issueYear" />
|
||||||
<Input form={form} handleChange={handleChange} label="材质" field="material" />
|
<Input form={form} handleChange={handleChange} label="材质" field="material" />
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,7 @@ export default function List() {
|
||||||
category: 'category',
|
category: 'category',
|
||||||
packaging: 'packaging',
|
packaging: 'packaging',
|
||||||
rarity: 'rarity',
|
rarity: 'rarity',
|
||||||
|
numberCategory: 'numberCategory',
|
||||||
version: 'version',
|
version: 'version',
|
||||||
gradingCompany: 'gradingCompany',
|
gradingCompany: 'gradingCompany',
|
||||||
gradingScore: 'gradingScore',
|
gradingScore: 'gradingScore',
|
||||||
|
|
@ -278,6 +279,11 @@ export default function List() {
|
||||||
const rarityOrder = { '通货': 1, '特色': 2, '少见': 3, '稀有': 4, '珍品': 5, '孤品': 6 }
|
const rarityOrder = { '通货': 1, '特色': 2, '少见': 3, '稀有': 4, '珍品': 5, '孤品': 6 }
|
||||||
aVal = rarityOrder[aVal] || 0
|
aVal = rarityOrder[aVal] || 0
|
||||||
bVal = rarityOrder[bVal] || 0
|
bVal = rarityOrder[bVal] || 0
|
||||||
|
} else if (sortField === 'numberCategory') {
|
||||||
|
// 号码分类按自定义顺序排序
|
||||||
|
const numberCategoryOrder = { '无2347': 1, '无347': 2, '无247': 3, '无47': 4, '无4': 5, '带4': 6, '其他': 7 }
|
||||||
|
aVal = numberCategoryOrder[aVal] || 99
|
||||||
|
bVal = numberCategoryOrder[bVal] || 99
|
||||||
}
|
}
|
||||||
if (aVal == null) return 1
|
if (aVal == null) return 1
|
||||||
if (bVal == null) return -1
|
if (bVal == null) return -1
|
||||||
|
|
@ -317,11 +323,25 @@ export default function List() {
|
||||||
return colors[category] || '#64748b'
|
return colors[category] || '#64748b'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getNumberCategoryColor = (cat) => {
|
||||||
|
const colors = { '无2347': '#8b5cf6', '无347': '#ef4444', '无247': '#fbbf24', '无47': '#3b82f6', '无4': '#06b6d4', '带4': '#22c55e', '其他': '#64748b' };
|
||||||
|
return colors[cat] || '#64748b';
|
||||||
|
};
|
||||||
|
|
||||||
const getRarityColor = (rarity) => {
|
const getRarityColor = (rarity) => {
|
||||||
const colors = { '通货': '#22c55e', '特色': '#06b6d4', '少见': '#3b82f6', '稀有': '#ec4899', '珍品': '#ef4444', '孤品': '#8b5cf6' }
|
const colors = { '通货': '#22c55e', '特色': '#06b6d4', '少见': '#3b82f6', '稀有': '#ec4899', '珍品': '#ef4444', '孤品': '#8b5cf6' }
|
||||||
return colors[rarity] || '#64748b'
|
return colors[rarity] || '#64748b'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const formatPrefixSerial = (serial) => {
|
||||||
|
if (!serial) return '-'
|
||||||
|
// 提取J开头的10位(1位J + 9位数字)
|
||||||
|
const match = serial.match(/J(\d{9})/)
|
||||||
|
if (match) return 'J' + match[1]
|
||||||
|
// 如果没有J开头,取前10位
|
||||||
|
return serial.substring(0, 10)
|
||||||
|
}
|
||||||
|
|
||||||
const getPackagingColor = (packaging) => {
|
const getPackagingColor = (packaging) => {
|
||||||
const colors = { '裸钞': '#22c55e', '单张': '#3b82f6', '标十': '#fbbf24', '标百': '#8b5cf6' }
|
const colors = { '裸钞': '#22c55e', '单张': '#3b82f6', '标十': '#fbbf24', '标百': '#8b5cf6' }
|
||||||
return colors[packaging] || '#64748b'
|
return colors[packaging] || '#64748b'
|
||||||
|
|
@ -365,26 +385,29 @@ export default function List() {
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '4px' }}>
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '4px' }}>
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: '6px', flexWrap: 'wrap' }}>
|
<div style={{ display: 'flex', alignItems: 'center', gap: '6px', flexWrap: 'wrap' }}>
|
||||||
<span style={{ color: '#fff', fontSize: '12px', fontWeight: 'normal' }}>{item.code || '-'}</span>
|
<span style={{ color: '#fff', fontSize: '12px', fontWeight: 'normal' }}>{item.code || '-'}</span>
|
||||||
<span style={{ color: '#fbbf24', fontSize: '14px', fontFamily: 'monospace', fontWeight: 'bold' }}>{item.prefixSerial || '-'}</span>
|
<span style={{ color: '#fbbf24', fontSize: '14px', fontFamily: 'monospace', }}>{formatPrefixSerial(item.prefixSerial)}</span>
|
||||||
{item.packaging && <span style={{ background: getPackagingColor(item.packaging) + '20', color: getPackagingColor(item.packaging), fontSize: '10px', padding: '1px 5px', borderRadius: '3px' }}>{item.packaging}</span>}
|
{item.packaging && <span style={{ background: getPackagingColor(item.packaging) + '20', color: getPackagingColor(item.packaging), fontSize: '10px', padding: '1px 5px', borderRadius: '3px' }}>{item.packaging}</span>}
|
||||||
|
{item.numberCategory && <span style={{ color: getNumberCategoryColor(item.numberCategory), fontSize: '9px', padding: '1px 4px', background: getNumberCategoryColor(item.numberCategory) + '20', borderRadius: '2px', marginLeft: '4px' }}>{item.numberCategory}</span>}
|
||||||
</div>
|
</div>
|
||||||
<div style={{ display: 'flex', gap: '3px' }}>
|
<div style={{ display: 'flex', gap: '3px' }}>
|
||||||
{item.status && <span style={{ color: getStatusColor(item.status), fontSize: '9px', padding: '1px 4px', background: getStatusColor(item.status) + '25', borderRadius: '2px' }}>{getStatusText(item.status)}</span>}
|
{item.status && <span style={{ color: getStatusColor(item.status), fontSize: '9px', padding: '1px 4px', background: getStatusColor(item.status) + '25', borderRadius: '2px' }}>{getStatusText(item.status)}</span>}
|
||||||
{item.category && <span style={{ color: getCategoryColor(item.category), fontSize: '9px', padding: '1px 4px', background: getCategoryColor(item.category) + '25', borderRadius: '2px' }}>{getCategoryText(item.category)}</span>}
|
{item.category && <span style={{ color: getCategoryColor(item.category), fontSize: '9px', padding: '1px 4px', background: getCategoryColor(item.category) + '25', borderRadius: '2px' }}>{getCategoryText(item.category)}</span>}
|
||||||
{item.rarity && <span style={{ color: getRarityColor(item.rarity), fontSize: '9px', padding: '1px 4px', background: getRarityColor(item.rarity) + '20', borderRadius: '2px' }}>{item.rarity}</span>}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* 第2行:版本(彩色) + 已评级 + 评级公司 + 评级分数 + 三星 + 特殊标识 | 备注 */}
|
{/* 第2行:版本(彩色) + 已评级 + 评级公司 + 评级分数 + 三星 + 特殊标识 | 备注 */}
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: '4px', flexWrap: 'wrap', marginBottom: '4px' }}>
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: '2px', flexWrap: 'wrap', marginBottom: '4px' }}>
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: '4px', flexWrap: 'wrap' }}>
|
<div style={{ display: 'flex', alignItems: 'center', gap: '2px', flexWrap: 'wrap' }}>
|
||||||
{item.version && <span style={{ background: getVersionColor(item.version).bg, color: getVersionColor(item.version).color, fontSize: '10px', padding: '1px 5px', borderRadius: '3px' }}>{item.version}</span>}
|
{item.version && <span style={{ background: getVersionColor(item.version).bg, color: getVersionColor(item.version).color, fontSize: '10px', padding: '1px 5px', borderRadius: '3px' }}>{item.version}</span>}
|
||||||
{item.isGraded && <span style={{ background: 'rgba(6, 182, 212, 0.15)', color: '#06b6d4', fontSize: '9px', padding: '1px 4px', borderRadius: '2px' }}>已评级</span>}
|
{item.isGraded && <span style={{ background: 'rgba(6, 182, 212, 0.15)', color: '#06b6d4', fontSize: '9px', padding: '1px 4px', borderRadius: '2px' }}>已评级</span>}
|
||||||
{item.gradingCompany && <span style={{ background: 'rgba(6, 182, 212, 0.15)', color: '#06b6d4', fontSize: '9px', padding: '1px 4px', borderRadius: '2px' }}>{item.gradingCompany.substring(0,4)}</span>}
|
{item.gradingCompany && <span style={{ background: 'rgba(6, 182, 212, 0.15)', color: '#06b6d4', fontSize: '9px', padding: '1px 4px', borderRadius: '2px' }}>{item.gradingCompany.substring(0,4)}</span>}
|
||||||
{item.gradingScore && <span style={{ background: 'rgba(249, 115, 22, 0.2)', color: '#f97316', fontSize: '9px', padding: '1px 4px', borderRadius: '2px', fontWeight: 'bold' }}>{item.gradingScore}</span>}
|
{item.gradingScore && <span style={{ background: 'rgba(249, 115, 22, 0.2)', color: '#f97316', fontSize: '9px', padding: '1px 4px', borderRadius: '2px', }}>{item.gradingScore}</span>}
|
||||||
{item.threeStar && <span style={{ background: 'rgba(6, 182, 212, 0.15)', color: '#06b6d4', fontSize: '9px', padding: '1px 4px', borderRadius: '2px' }}>三星</span>}
|
{item.threeStar && <span style={{ background: 'rgba(6, 182, 212, 0.15)', color: '#06b6d4', fontSize: '9px', padding: '1px 4px', borderRadius: '2px' }}>三星</span>}
|
||||||
{item.specialMark && <span style={{ background: 'rgba(139, 92, 246, 0.15)', color: '#a78bfa', fontSize: '9px', padding: '1px 4px', borderRadius: '2px' }}>{item.specialMark}</span>}
|
{item.specialMark && <span style={{ background: 'rgba(139, 92, 246, 0.15)', color: '#a78bfa', fontSize: '9px', padding: '1px 4px', borderRadius: '2px' }}>{item.specialMark}</span>}
|
||||||
</div>
|
</div>
|
||||||
{item.remark && <span style={{ background: 'rgba(100,116,139,0.2)', color: '#94a3b8', fontSize: '9px', padding: '1px 4px', borderRadius: '2px', maxWidth: '80px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{item.remark}</span>}
|
<div style={{ display: 'flex', gap: '2px', marginLeft: 'auto' }}>
|
||||||
|
{item.rarity && <span style={{ color: getRarityColor(item.rarity), fontSize: '9px', padding: '1px 4px', background: getRarityColor(item.rarity) + '20', borderRadius: '2px' }}>{item.rarity}</span>}
|
||||||
|
{item.remark && <span style={{ background: 'rgba(100,116,139,0.2)', color: '#94a3b8', fontSize: '9px', padding: '1px 4px', borderRadius: '2px', maxWidth: '80px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{item.remark}</span>}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* 第3行:成本 + 修复 + 评级 | 目标 + 出售 */}
|
{/* 第3行:成本 + 修复 + 评级 | 目标 + 出售 */}
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between', fontSize: '10px', color: '#94a3b8', paddingTop: '4px', borderTop: '1px solid rgba(255,255,255,0.05)' }}>
|
<div style={{ display: 'flex', justifyContent: 'space-between', fontSize: '10px', color: '#94a3b8', paddingTop: '4px', borderTop: '1px solid rgba(255,255,255,0.05)' }}>
|
||||||
|
|
@ -405,7 +428,7 @@ export default function List() {
|
||||||
<div style={{ minHeight: '100vh', overflowY: 'auto', background: '#0f172a', paddingBottom: '70px' }}>
|
<div style={{ minHeight: '100vh', overflowY: 'auto', background: '#0f172a', paddingBottom: '70px' }}>
|
||||||
<div style={{ padding: '20px', background: 'rgba(255,255,255,0.02)', borderBottom: '1px solid rgba(255,255,255,0.05)' }}>
|
<div style={{ padding: '20px', background: 'rgba(255,255,255,0.02)', borderBottom: '1px solid rgba(255,255,255,0.05)' }}>
|
||||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '16px' }}>
|
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '16px' }}>
|
||||||
<div style={{ color: '#fff', fontSize: '20px', fontWeight: 'bold' }}>我的藏品 <span style={{ fontSize: '14px', color: '#fbbf24' }}>({filteredCollections.length})</span></div>
|
<div style={{ color: '#fff', fontSize: '20px', }}>我的藏品 <span style={{ fontSize: '14px', color: '#fbbf24' }}>({filteredCollections.length})</span></div>
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
|
<div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
|
||||||
{filter && filterType && (
|
{filter && filterType && (
|
||||||
<button
|
<button
|
||||||
|
|
@ -425,9 +448,9 @@ export default function List() {
|
||||||
|
|
||||||
{filter && filterType && (
|
{filter && filterType && (
|
||||||
<div style={{ background: 'rgba(59, 130, 246, 0.1)', border: '1px solid rgba(59, 130, 246, 0.3)', borderRadius: '8px', padding: '12px', marginBottom: '16px' }}>
|
<div style={{ background: 'rgba(59, 130, 246, 0.1)', border: '1px solid rgba(59, 130, 246, 0.3)', borderRadius: '8px', padding: '12px', marginBottom: '16px' }}>
|
||||||
<div style={{ color: '#60a5fa', fontSize: '13px', fontWeight: 'bold' }}>当前筛选:</div>
|
<div style={{ color: '#60a5fa', fontSize: '13px', }}>当前筛选:</div>
|
||||||
<div style={{ color: '#fff', fontSize: '14px', marginTop: '4px' }}>
|
<div style={{ color: '#fff', fontSize: '14px', marginTop: '4px' }}>
|
||||||
{getFilterLabel(filterType)} = <span style={{ color: '#fbbf24', fontWeight: 'bold' }}>{getFilterValueLabel(filterType, filter)}</span>
|
{getFilterLabel(filterType)} = <span style={{ color: '#fbbf24', }}>{getFilterValueLabel(filterType, filter)}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -479,7 +502,7 @@ export default function List() {
|
||||||
|
|
||||||
{/* 排序表头按钮 */}
|
{/* 排序表头按钮 */}
|
||||||
<div style={{ display: 'flex', gap: '6px', alignItems: 'center', flexWrap: 'wrap', marginBottom: '12px' }}>
|
<div style={{ display: 'flex', gap: '6px', alignItems: 'center', flexWrap: 'wrap', marginBottom: '12px' }}>
|
||||||
<span style={{ color: '#94a3b8', fontSize: '13px', marginRight: '4px' }}>排序:</span>
|
<span style={{ color: '#94a3b8', fontSize: '13px', marginRight: '2px' }}>排序:</span>
|
||||||
{[
|
{[
|
||||||
{ key: 'code', label: '编号' },
|
{ key: 'code', label: '编号' },
|
||||||
{ key: 'prefixSerial', label: '冠字号' },
|
{ key: 'prefixSerial', label: '冠字号' },
|
||||||
|
|
@ -487,6 +510,7 @@ export default function List() {
|
||||||
{ key: 'goalPrice', label: '售价' },
|
{ key: 'goalPrice', label: '售价' },
|
||||||
{ key: 'category', label: '持仓类型' },
|
{ key: 'category', label: '持仓类型' },
|
||||||
{ key: 'rarity', label: '珍惜度' },
|
{ key: 'rarity', label: '珍惜度' },
|
||||||
|
{ key: 'numberCategory', label: '号码分类' },
|
||||||
{ key: 'gradingScore', label: '评级分数' }
|
{ key: 'gradingScore', label: '评级分数' }
|
||||||
].map(item => (
|
].map(item => (
|
||||||
<div key={item.key} onClick={() => {
|
<div key={item.key} onClick={() => {
|
||||||
|
|
@ -523,9 +547,9 @@ export default function List() {
|
||||||
<div key={item.id} onClick={() => goDetail(item.id)} style={{ background: 'rgba(255,255,255,0.03)', border: '1px solid rgba(255,255,255,0.05)', borderRadius: '12px', padding: '12px', cursor: 'pointer' }}>
|
<div key={item.id} onClick={() => goDetail(item.id)} style={{ background: 'rgba(255,255,255,0.03)', border: '1px solid rgba(255,255,255,0.05)', borderRadius: '12px', padding: '12px', cursor: 'pointer' }}>
|
||||||
<div style={{ height: '80px', background: 'rgba(255,255,255,0.03)', borderRadius: '8px', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: '32px', marginBottom: '10px' }}>🐉</div>
|
<div style={{ height: '80px', background: 'rgba(255,255,255,0.03)', borderRadius: '8px', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: '32px', marginBottom: '10px' }}>🐉</div>
|
||||||
<div style={{ color: '#fff', fontSize: '12px', fontWeight: 'normal' }}>{item.code || '-'}</div>
|
<div style={{ color: '#fff', fontSize: '12px', fontWeight: 'normal' }}>{item.code || '-'}</div>
|
||||||
<div style={{ color: '#fbbf24', fontSize: '12px', fontFamily: 'monospace', marginTop: '2px' }}>{item.prefixSerial || '-'}</div>
|
<div style={{ color: '#fbbf24', fontSize: '12px', fontFamily: 'monospace', marginTop: '2px' }}>{formatPrefixSerial(item.prefixSerial)}</div>
|
||||||
<div style={{ display: 'flex', gap: '4px', marginTop: '6px', flexWrap: 'wrap' }}>
|
<div style={{ display: 'flex', gap: '2px', marginTop: '6px', flexWrap: 'wrap' }}>
|
||||||
{item.gradingScore && <span style={{ color: '#fbbf24', fontSize: '12px', fontWeight: 'bold' }}>{item.gradingScore}</span>}
|
{item.gradingScore && <span style={{ color: '#fbbf24', fontSize: '12px', }}>{item.gradingScore}</span>}
|
||||||
{item.threeStar && <span style={{ color: '#fbbf24', fontSize: '10px' }}>⭐⭐⭐</span>}
|
{item.threeStar && <span style={{ color: '#fbbf24', fontSize: '10px' }}>⭐⭐⭐</span>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ export default function Stats() {
|
||||||
byGrading: [],
|
byGrading: [],
|
||||||
byPackaging: [],
|
byPackaging: [],
|
||||||
byRarity: [],
|
byRarity: [],
|
||||||
|
byNumberCategory: [],
|
||||||
byVersion: [],
|
byVersion: [],
|
||||||
byGradingCompany: [],
|
byGradingCompany: [],
|
||||||
byGradingScore: [],
|
byGradingScore: [],
|
||||||
|
|
@ -49,6 +50,7 @@ export default function Stats() {
|
||||||
byGrading: data.byGrading || [],
|
byGrading: data.byGrading || [],
|
||||||
byPackaging: data.byPackaging || [],
|
byPackaging: data.byPackaging || [],
|
||||||
byRarity: data.byRarity || [],
|
byRarity: data.byRarity || [],
|
||||||
|
byNumberCategory: data.byNumberCategory || [],
|
||||||
byVersion: data.byVersion || [],
|
byVersion: data.byVersion || [],
|
||||||
byGradingCompany: data.byGradingCompany || [],
|
byGradingCompany: data.byGradingCompany || [],
|
||||||
byGradingScore: data.byGradingScore || [],
|
byGradingScore: data.byGradingScore || [],
|
||||||
|
|
@ -94,10 +96,15 @@ export default function Stats() {
|
||||||
// 颜色配置
|
// 颜色配置
|
||||||
const colors = {
|
const colors = {
|
||||||
packaging: { '标十': '#22c55e', '标百': '#3b82f6', '单张': '#f59e0b', '裸钞': '#64748b' },
|
packaging: { '标十': '#22c55e', '标百': '#3b82f6', '单张': '#f59e0b', '裸钞': '#64748b' },
|
||||||
rarity: { '通货': '#64748b', '特色': '#22c55e', '少见': '#3b82f6', '稀有': '#8b5cf6', '珍品': '#f59e0b', '孤品': '#ef4444' },
|
rarity: { '通货': '#64748b', '特色': '#22c55e', '少见': '#3b82f6', '稀有': '#8b5cf6', '珍品': '#ef4444', '孤品': '#fbbf24' },
|
||||||
status: { 'in_collection': '#22c55e', 'selling': '#f59e0b', 'sold': '#ef4444', 'grading': '#8b5cf6', 'repairing': '#f97316', 'transit': '#06b6d4', 'seeking': '#ec4899' },
|
status: { 'in_collection': '#22c55e', 'selling': '#f59e0b', 'sold': '#ef4444', 'grading': '#8b5cf6', 'repairing': '#f97316', 'transit': '#06b6d4', 'seeking': '#ec4899' },
|
||||||
category: { '自持': '#22c55e', '寄存': '#3b82f6', '寄售': '#f59e0b', '共有': '#8b5cf6', '寻号': '#06b6d4', '生肖钞': '#22c55e', '其他': '#64748b' },
|
category: { '自持': '#22c55e', '寄存': '#3b82f6', '寄售': '#f59e0b', '共有': '#8b5cf6', '寻号': '#06b6d4', '生肖钞': '#22c55e', '其他': '#64748b' },
|
||||||
profitLoss: { 'profit': '#22c55e', 'loss': '#ef4444' }
|
profitLoss: { 'profit': '#22c55e', 'loss': '#ef4444' },
|
||||||
|
numberCategory: { '无2347': '#8b5cf6', '无347': '#ef4444', '无247': '#fbbf24', '无47': '#3b82f6', '无4': '#06b6d4', '带4': '#22c55e', '其他': '#64748b' },
|
||||||
|
version: {},
|
||||||
|
gradingCompany: {},
|
||||||
|
gradingScore: {},
|
||||||
|
specialMark: {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 标签映射
|
// 标签映射
|
||||||
|
|
@ -117,8 +124,14 @@ export default function Stats() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const colorPalette = ['#22c55e', '#3b82f6', '#f59e0b', '#ef4444', '#8b5cf6', '#06b6d4', '#ec4899', '#f97316', '#14b8a6', '#a855f7']
|
||||||
const getColor = (type, value) => {
|
const getColor = (type, value) => {
|
||||||
return colors[type]?.[value] || '#64748b'
|
if (colors[type]?.[value]) return colors[type][value]
|
||||||
|
// 动态生成颜色
|
||||||
|
const key = String(value)
|
||||||
|
let hash = 0
|
||||||
|
for (let i = 0; i < key.length; i++) hash = key.charCodeAt(i) + ((hash << 5) - hash)
|
||||||
|
return colorPalette[Math.abs(hash) % colorPalette.length]
|
||||||
}
|
}
|
||||||
|
|
||||||
const getLabel = (type, value) => {
|
const getLabel = (type, value) => {
|
||||||
|
|
@ -130,11 +143,23 @@ export default function Stats() {
|
||||||
return Number(val).toLocaleString('zh-CN')
|
return Number(val).toLocaleString('zh-CN')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const numberCategoryOrder = ['无2347', '无347', '无247', '无47', '无4', '带4', '其他']
|
||||||
|
const getSortedData = (data, type) => {
|
||||||
|
if (type === 'numberCategory') {
|
||||||
|
return [...data].sort((a, b) => {
|
||||||
|
const order = numberCategoryOrder.indexOf(a.numberCategory)
|
||||||
|
const order2 = numberCategoryOrder.indexOf(b.numberCategory)
|
||||||
|
return order - order2
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
const DistributionCard = ({ title, data, type, valueKey, labelKey }) => (
|
const DistributionCard = ({ title, data, type, valueKey, labelKey }) => (
|
||||||
<div style={{ background: 'rgba(255,255,255,0.03)', borderRadius: '12px', padding: '16px', marginBottom: '12px' }}>
|
<div style={{ background: 'rgba(255,255,255,0.03)', borderRadius: '12px', padding: '16px', marginBottom: '12px' }}>
|
||||||
<div style={{ color: '#fbbf24', fontSize: '14px', fontWeight: 'bold', marginBottom: '12px' }}>{title}</div>
|
<div style={{ color: '#fbbf24', fontSize: '14px', fontWeight: 'bold', marginBottom: '12px' }}>{title}</div>
|
||||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: '8px' }}>
|
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: '8px' }}>
|
||||||
{data.slice(0, 6).map((item, index) => {
|
{getSortedData(data, type).slice(0, 6).map((item, index) => {
|
||||||
const value = item[valueKey]
|
const value = item[valueKey]
|
||||||
const label = getLabel(type, item[labelKey] || value)
|
const label = getLabel(type, item[labelKey] || value)
|
||||||
const color = getColor(type, value)
|
const color = getColor(type, value)
|
||||||
|
|
@ -159,13 +184,17 @@ export default function Stats() {
|
||||||
e.currentTarget.style.borderColor = 'rgba(255,255,255,0.05)'
|
e.currentTarget.style.borderColor = 'rgba(255,255,255,0.05)'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: '6px', marginBottom: '4px' }}>
|
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||||
<div style={{ width: '8px', height: '8px', borderRadius: '2px', background: color }} />
|
<div style={{ display: 'flex', alignItems: 'center', gap: '6px', flex: 1, overflow: 'hidden' }}>
|
||||||
<div style={{ color: '#fff', fontSize: '13px', fontWeight: '500', flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
|
<div style={{ width: '8px', height: '8px', borderRadius: '2px', background: color, flexShrink: 0 }} />
|
||||||
{label}
|
<div style={{ color: color, fontSize: '12px', fontWeight: '500', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
|
||||||
|
{label}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style={{ color: '#fff', fontSize: '14px', fontWeight: 'bold', marginLeft: '8px', flexShrink: 0 }}>
|
||||||
|
{item.count}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ color: '#fff', fontSize: '16px', fontWeight: 'bold' }}>{item.count}</div>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
|
@ -246,9 +275,9 @@ export default function Stats() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DistributionCard title="📋 状态分布" data={stats.byStatus} type="status" valueKey="status" />
|
|
||||||
<DistributionCard title="💼 持仓类型分布" data={stats.byCategory} type="category" valueKey="category" />
|
|
||||||
<DistributionCard title="📦 包装分布" data={stats.byPackaging} type="packaging" valueKey="packaging" />
|
<DistributionCard title="📦 包装分布" data={stats.byPackaging} type="packaging" valueKey="packaging" />
|
||||||
|
<DistributionCard title="🔢 号码分类分布" data={stats.byNumberCategory} type="numberCategory" valueKey="numberCategory" />
|
||||||
|
<DistributionCard title="📋 状态分布" data={stats.byStatus} type="status" valueKey="status" />
|
||||||
<DistributionCard title="⭐ 珍惜度分布" data={stats.byRarity} type="rarity" valueKey="rarity" />
|
<DistributionCard title="⭐ 珍惜度分布" data={stats.byRarity} type="rarity" valueKey="rarity" />
|
||||||
<DistributionCard title="🏷️ 版别分布" data={stats.byVersion} type="version" valueKey="version" />
|
<DistributionCard title="🏷️ 版别分布" data={stats.byVersion} type="version" valueKey="version" />
|
||||||
<DistributionCard title="🏅 评级机构分布" data={stats.byGradingCompany} type="gradingCompany" valueKey="company" />
|
<DistributionCard title="🏅 评级机构分布" data={stats.byGradingCompany} type="gradingCompany" valueKey="company" />
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue