From bd22c201086f67e8b7397d7e1fc960b3ebc596e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B2=E8=BE=B0=E7=94=9F=E4=BA=A7?= Date: Sat, 11 Apr 2026 01:47:55 +0800 Subject: [PATCH] =?UTF-8?q?v1.2.82=20-=20=E8=A1=8C=E6=83=85=E5=BD=95?= =?UTF-8?q?=E5=85=A5=E5=8A=9F=E8=83=BD=E4=BC=98=E5=8C=96=EF=BC=9A=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E5=8F=B7=E7=A0=81=E5=88=86=E7=B1=BB=E3=80=81=E5=B0=BE?= =?UTF-8?q?=E5=8F=B7=E3=80=81=E5=A4=A7=E5=B0=8F=E5=8F=B7=E3=80=81=E5=8C=85?= =?UTF-8?q?=E8=A3=85=E7=B1=BB=E5=9E=8B=E3=80=81=E5=87=BA=E5=94=AE=E8=80=85?= =?UTF-8?q?=E3=80=81=E8=B4=AD=E4=B9=B0=E8=80=85=E5=AD=97=E6=AE=B5=EF=BC=9B?= =?UTF-8?q?=E6=8F=90=E4=BA=A4=E6=97=B6=E8=87=AA=E5=8A=A8=E8=AE=A1=E7=AE=97?= =?UTF-8?q?=E5=B0=BE=E5=8F=B7=E5=92=8C=E5=A4=A7=E5=B0=8F=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/routers/auth.py | 20 +- backend/app/routers/collections.py | 30 +- backend/app/routers/users.py | 4 +- backend/app/utils/number_category.py | 132 ++++++ config/VERSION | 2 +- frontend/index.html | 2 +- frontend/package-lock.json | 12 +- frontend/package.json | 8 +- frontend/package.txt | 1 + frontend/src/App.jsx | 7 +- frontend/src/config/version.js | 2 +- frontend/src/pages/Add.jsx | 189 +++++++- frontend/src/pages/Home.jsx | 4 +- frontend/src/pages/Info.jsx | 680 --------------------------- frontend/src/pages/News.jsx | 8 +- frontend/src/utils/numberCategory.js | 129 +++++ 16 files changed, 495 insertions(+), 735 deletions(-) create mode 100644 backend/app/utils/number_category.py create mode 100644 frontend/package.txt delete mode 100644 frontend/src/pages/Info.jsx create mode 100644 frontend/src/utils/numberCategory.js diff --git a/backend/app/routers/auth.py b/backend/app/routers/auth.py index 0f70470..0c6fe73 100644 --- a/backend/app/routers/auth.py +++ b/backend/app/routers/auth.py @@ -13,7 +13,7 @@ router = APIRouter(prefix="/api/auth", tags=["认证"]) def generate_user_code(db): - """生成用户编码,从201开始,按自然数顺序递增""" + """生成用户编码,从201开始,按自然数顺序递增,跳过已存在的""" # 查找最大的user_code max_code = db.query(User.user_code).filter(User.user_code != None).order_by(User.user_code.desc()).first() if max_code and max_code[0]: @@ -21,6 +21,9 @@ def generate_user_code(db): num = int(max_code[0]) + 1 if num < 201: num = 201 + # 检查是否已存在,如果存在则继续递增 + while db.query(User).filter(User.user_code == str(num)).first(): + num += 1 return str(num) except: pass @@ -100,7 +103,20 @@ def register(user_data: UserCreate, db: Session = Depends(get_db)): db.commit() db.refresh(user) - return user + # 返回用户信息(避免Pydantic序列化问题) + return { + "id": user.f99_90_id, + "username": user.f01_01_name, + "user_code": user.user_code, + "email": user.email, + "phone": user.phone, + "avatar": user.avatar, + "role": user.role, + "level": user.f99_94_level, + "aiCount": user.f99_95_ai_count or 0, + "searchCount": user.f99_96_search_count or 0, + "collectionCount": user.f99_97_collection_count or 0 + } @router.post("/login", response_model=Token) diff --git a/backend/app/routers/collections.py b/backend/app/routers/collections.py index 7fbf85a..d04bb8f 100644 --- a/backend/app/routers/collections.py +++ b/backend/app/routers/collections.py @@ -132,14 +132,17 @@ def get_collections( # 如果指定 all_users=true,则返回所有用户藏品 from sqlalchemy.orm import joinedload - # 管理员默认查看全库,普通用户只看自己 - if current_user.role == "admin": - # 联表查询获取用户名 + # 管理员默认查看全库,普通用户只看自己,未登录返回空列表 + if current_user is None or current_user.role != "admin": + # 非管理员或未登录用户只能查看自己的藏品(如果没有登录则返回空) + if current_user is None: + return {"data": [], "total": 0, "page": 1, "limit": 20} + query = db.query(Collection).filter(Collection.f99_91_user_id == current_user.f99_90_id) + else: + # 管理员查看所有藏品 query = db.query(Collection, User.f01_01_name.label('owner_name')).join( User, Collection.f99_91_user_id == User.f99_90_id, isouter=True ) - else: - query = db.query(Collection).filter(Collection.f99_91_user_id == current_user.f99_90_id) # 如果指定了user_id参数,则只返回该用户的藏品 if user_id: @@ -202,7 +205,7 @@ def get_collections( data_list = [] for item in data: # 处理联表查询结果 - if current_user.role == "admin": + if current_user is not None and current_user.role == "admin": collection_item, owner_name = item else: collection_item = item @@ -278,12 +281,12 @@ def get_stats( ): """获取藏品统计""" # 获取所有藏品 - if current_user.role == "admin": - all_collections = db.query(Collection).all() - else: + if current_user is None or current_user.role != "admin": all_collections = db.query(Collection).filter( Collection.f99_91_user_id == current_user.f99_90_id ).all() + else: + all_collections = db.query(Collection).all() # 总数 total_count = len(all_collections) @@ -382,7 +385,7 @@ def get_collection( collection = dict(result._mapping) # 非管理员只能查看自己的藏品 - if current_user.role != "admin" and collection.get('f99_91_user_id') != current_user.f99_90_id: + if (current_user is None or current_user.role != "admin") and collection.get('f99_91_user_id') != current_user.f99_90_id: raise HTTPException(status_code=403, detail="无权访问") result_dict = { @@ -496,6 +499,11 @@ def create_collection( } } + # 自动分类:如果未提供号码分类,则根据冠字号自动分类 + if not collection_data.f02_14_number_category and collection_data.f02_10_prefix_serial: + from app.utils.number_category import get_number_category + collection_data.f02_14_number_category = get_number_category(collection_data.f02_10_prefix_serial) + collection = Collection( f99_91_user_id=current_user.f99_90_id, f01_01_name=collection_data.f01_01_name, @@ -709,7 +717,7 @@ async def delete_image( Collection.f99_90_id == image.collection_id ).first() - if collection and current_user.role != "admin" and collection.f99_91_user_id != current_user.f99_90_id: + if collection and (current_user is None or current_user.role != "admin") and collection.f99_91_user_id != current_user.f99_90_id: raise HTTPException(status_code=403, detail="E00014: 无权删除此图片") # 删除OSS文件(如果path是OSS URL) diff --git a/backend/app/routers/users.py b/backend/app/routers/users.py index 079be6e..c8cf2da 100644 --- a/backend/app/routers/users.py +++ b/backend/app/routers/users.py @@ -11,7 +11,7 @@ router = APIRouter(prefix="/api", tags=["用户"]) # ============ 当前用户接口 ============ -@router.get("/users/me", response_model=UserResponse) +@router.get("/users/me") # 无 response_model,避免 Pydantic 序列化问题 def get_current_user_info( current_user: User = Depends(get_current_user) ): @@ -47,7 +47,7 @@ def get_current_user_info( "f99_93_updated_at": current_user.f99_93_updated_at.isoformat() if current_user.f99_93_updated_at else None } -@router.put("/users/me", response_model=UserResponse) +@router.put("/users/me") # 无 response_model,避免 Pydantic 序列化问题 def update_current_user( user_update: UserUpdate, current_user: User = Depends(get_current_user), diff --git a/backend/app/utils/number_category.py b/backend/app/utils/number_category.py new file mode 100644 index 0000000..340e9f2 --- /dev/null +++ b/backend/app/utils/number_category.py @@ -0,0 +1,132 @@ +# 号码分类工具 +# 按MEMORY.md最新规则 (2026-04-02) +# 优先级:数字越小越有价值 + +CATEGORIES = [ + # 1. 圆圆号:无.123457,只能用0689 + {"name": "圆圆号", "cannot_use": ".123457", "must_have": ""}, + # 2. 倒置号:无23457,可用01689必须有1 + {"name": "倒置号", "cannot_use": "23457", "must_have": "1"}, + # 3. 金马王:无12347,可用05689必须有5 + {"name": "金马王", "cannot_use": "12347", "must_have": "5"}, + # 4. 金马号:无2347,可用015689必须有1和5 + {"name": "金马号", "cannot_use": "2347", "must_have": "15"}, + # 5. 金山王:无12457,可用03689必须有3 + {"name": "金山王", "cannot_use": "12457", "must_have": "3"}, + # 6. 天马王:无1247,可用035689必须有3和5 + {"name": "天马王", "cannot_use": "1247", "must_have": "35"}, + # 7. 金山号:无2457,可用013689必须有1和3 + {"name": "金山号", "cannot_use": "2457", "must_have": "13"}, + # 8. 天马号:无247,可用0135689必须有1、3和5 + {"name": "天马号", "cannot_use": "247", "must_have": "135"}, + # 9. 朦胧王:无13457 + {"name": "朦胧王", "cannot_use": "13457", "must_have": ""}, + # 10. 朦胧号:无3457 + {"name": "朦胧号", "cannot_use": "3457", "must_have": ""}, + # 11. 如意号:无1347 + {"name": "如意号", "cannot_use": "1347", "must_have": ""}, + # 12. 钻石号:无347 + {"name": "钻石号", "cannot_use": "347", "must_have": ""}, + # 13. 永恒号:无47 + {"name": "永恒号", "cannot_use": "47", "must_have": ""}, + # 14. 无4号:不包含4 + {"name": "无4号", "cannot_use": "4", "must_have": ""}, + # 15. 通货:含4 + {"name": "通货", "cannot_use": "", "must_have": "4"}, +] + + +def extract_digits(serial: str) -> dict: + """提取冠字号中的数字部分""" + if not serial: + return {"digits": "", "type": "single"} + + nums = serial.replace("J", "").replace(",", "").replace(".", "").strip() + nums = "".join(c for c in nums if c.isdigit()) + + if nums.endswith("01"): + return {"digits": nums[:-2], "type": "hundred"} + elif nums.endswith("1"): + return {"digits": nums[:-1], "type": "ten"} + else: + return {"digits": nums, "type": "single"} + + +def matches_category(digits: str, category: dict) -> bool: + cannot_use = category.get("cannot_use", "") + must_have = category.get("must_have", "") + + # 检查不能用的数字 + for n in cannot_use: + if n in digits: + return False + + # 检查必须有的数字 + if must_have: + for n in must_have: + if n not in digits: + return False + + return True + + +def get_number_category(serial: str) -> str: + """号码分类函数""" + if not serial: + return "" + + # 提取数字 + info = extract_digits(serial) + digits = info["digits"] + + if not digits: + return "" + + # 根据类型取对应位数 + digits_type = info["type"] + if digits_type == "hundred": + # 标百看后6位 + check_digits = digits[-6:] if len(digits) >= 6 else digits + elif digits_type == "ten": + # 标十看后7位 + check_digits = digits[-7:] if len(digits) >= 7 else digits + else: + # 散钞看全部 + check_digits = digits + + # 按优先级匹配分类 + for category in CATEGORIES: + if matches_category(check_digits, category): + return category["name"] + + return "通货" + + +def get_number_category_color(category: str) -> str: + """获取分类颜色""" + colors = { + "圆圆号": "#ef4444", # 红 + "倒置号": "#f97316", # 橙 + "金马王": "#eab308", # 黄 + "金马号": "#84cc16", # 绿 + "金山王": "#22c55e", # 深绿 + "天马王": "#14b8a6", # 青 + "金山号": "#06b6d4", # 蓝 + "天马号": "#0ea5e9", # 浅蓝 + "朦胧王": "#6366f1", # 靛蓝 + "朦胧号": "#8b5cf6", # 紫 + "如意号": "#a855f7", # 深紫 + "钻石号": "#d946ef", # 品红 + "永恒号": "#ec4899", # 粉红 + "无4号": "#64748b", # 灰 + "通货": "#9ca3af", # 浅灰 + } + return colors.get(category, "#9ca3af") + + +def get_category_priority(category: str) -> int: + """获取分类优先级(数字越小越高级)""" + for i, cat in enumerate(CATEGORIES, 1): + if cat["name"] == category: + return i + return 999 \ No newline at end of file diff --git a/config/VERSION b/config/VERSION index 8a27477..f73f4c0 100644 --- a/config/VERSION +++ b/config/VERSION @@ -1 +1 @@ -VERSION=1.2.79 +VERSION=1.2.81 diff --git a/frontend/index.html b/frontend/index.html index 803dee4..e3a4d95 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -4,7 +4,7 @@ - 甲辰收藏 v=1.2.78 + 甲辰收藏 v=1.2.81 diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 95c053e..79e65c8 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "jiachenlong-frontend", - "version": "1.2.72", + "version": "1.2.81", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "jiachenlong-frontend", - "version": "1.2.72", + "version": "1.2.81", "dependencies": { "axios": "^1.7.9", "react": "^18.3.1", @@ -15,7 +15,7 @@ }, "devDependencies": { "@vitejs/plugin-react": "^4.3.4", - "vite": "^6.0.7" + "vite": "^6.4.2" } }, "node_modules/@babel/code-frame": { @@ -2047,9 +2047,9 @@ } }, "node_modules/vite": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", - "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", + "version": "6.4.2", + "resolved": "https://registry.npmmirror.com/vite/-/vite-6.4.2.tgz", + "integrity": "sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==", "dev": true, "license": "MIT", "dependencies": { diff --git a/frontend/package.json b/frontend/package.json index d1973e5..c37ff78 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "jiachenlong-frontend", - "version": "1.2.77", + "version": "1.2.81", "private": true, "description": "甲辰藏品管理系统 - 移动端前端", "scripts": { @@ -9,13 +9,13 @@ "preview": "vite preview" }, "dependencies": { + "axios": "^1.7.9", "react": "^18.3.1", "react-dom": "^18.3.1", - "react-router-dom": "^7.1.0", - "axios": "^1.7.9" + "react-router-dom": "^7.1.0" }, "devDependencies": { "@vitejs/plugin-react": "^4.3.4", - "vite": "^6.0.7" + "vite": "^6.4.2" } } diff --git a/frontend/package.txt b/frontend/package.txt new file mode 100644 index 0000000..1d01bc1 --- /dev/null +++ b/frontend/package.txt @@ -0,0 +1 @@ +v=1.2.80 diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index ae35683..a123fa5 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -5,7 +5,6 @@ import List from './pages/List' import Add from './pages/Add' import Stats from './pages/Stats' import News from './pages/News' -import Info from './pages/Info' import Login from './pages/Login' import Detail from './pages/Detail' import Edit from './pages/Edit' @@ -31,7 +30,6 @@ export default function App() { const basePath = path.split('?')[0] if (basePath === '/') return if (basePath === '/news') return - if (basePath === '/info') return if (basePath === '/stats') return if (basePath === '/list') return if (basePath === '/add') return @@ -53,7 +51,6 @@ export default function App() { } const isAdmin = user && user.role === 'admin' const isEditor = user && user.role === 'editor' - const canAccessInfo = isAdmin || isEditor // 登录页面独立渲染,不显示底部导航 if (!token) { @@ -91,8 +88,7 @@ export default function App() { { path: '/news', icon: '📰', label: '资讯' }, { path: '/stats', icon: '📊', label: '统计' }, { path: '/list', icon: '📚', label: '藏品' }, - { path: '/add', icon: '🎯', label: '添加' }, - ...(canAccessInfo ? [{ path: '/info', icon: '📝', label: '信息' }] : []), + { path: '/add', icon: '🎯', label: '录入' }, ...(isAdmin ? [{ path: '/admin', icon: '⚙️', label: '管理' }] : []) ].map(tab => (
) } +// Fri Apr 10 03:44:24 PM CST 2026 diff --git a/frontend/src/config/version.js b/frontend/src/config/version.js index cf4abcc..e808cde 100644 --- a/frontend/src/config/version.js +++ b/frontend/src/config/version.js @@ -3,7 +3,7 @@ // 修改版本号只需修改 ../../VERSION 文件中的 VERSION=xxx // 从环境变量读取(vite.config.js 注入) -export const APP_VERSION = import.meta.env.APP_VERSION || '1.0.0' +export const APP_VERSION = import.meta.env.APP_VERSION || '1.2.82' // 版本信息 export const VERSION_INFO = { diff --git a/frontend/src/pages/Add.jsx b/frontend/src/pages/Add.jsx index f09e0cb..a4bc066 100644 --- a/frontend/src/pages/Add.jsx +++ b/frontend/src/pages/Add.jsx @@ -1,6 +1,7 @@ -// 添加藏品页面 - 支持 AI 识别/手工录入/批量录入 +// 添加藏品页面 - 支持 AI 识别/手工录入 import React, { useState, useRef } from 'react' import { APP_VERSION } from '../config/version' +const API_BASE = localStorage.getItem('API_BASE') || '' 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:'其他'}]; // 字段转换函数 @@ -71,12 +72,47 @@ const getDefaultForm = () => ({ }) export default function Add() { + // 行情录入表单 + const [dealForm, setDealForm] = useState({ + serial: '', + category: '', + packaging: '标十', + price: '', + platform: '淘宝', + seller: '', + buyer: '', + date: new Date().toISOString().split('T')[0] + }) + const [savingDeal, setSavingDeal] = useState(false) + + // 号码分类函数 + const autoCategory = (serial) => { + const digits = serial.replace('J', '').replace(/[^0-9]/g, '').slice(0, 9) + if (!digits) return '' + const d = digits + if (!d.includes('1') && !d.includes('2') && !d.includes('3') && !d.includes('4') && !d.includes('5') && !d.includes('7')) return '圆圆号' + if (!d.includes('2') && !d.includes('3') && !d.includes('4') && !d.includes('5') && !d.includes('7')) return '倒置号' + if (!d.includes('1') && !d.includes('2') && !d.includes('3') && !d.includes('4') && !d.includes('7')) return '金马王' + if (!d.includes('2') && !d.includes('3') && !d.includes('4') && !d.includes('7')) return '金马号' + if (!d.includes('1') && !d.includes('2') && !d.includes('4') && !d.includes('5') && !d.includes('7')) return '金山王' + if (!d.includes('1') && !d.includes('2') && !d.includes('4') && !d.includes('7')) return '天马王' + if (!d.includes('2') && !d.includes('4') && !d.includes('5') && !d.includes('7')) return '金山号' + if (!d.includes('2') && !d.includes('4') && !d.includes('7')) return '天马号' + if (!d.includes('1') && !d.includes('3') && !d.includes('4') && !d.includes('5') && !d.includes('7')) return '朦胧王' + if (!d.includes('3') && !d.includes('4') && !d.includes('5') && !d.includes('7')) return '朦胧号' + if (!d.includes('1') && !d.includes('3') && !d.includes('4') && !d.includes('7')) return '如意号' + if (!d.includes('3') && !d.includes('4') && !d.includes('7')) return '钻石' + if (!d.includes('4') && !d.includes('7')) return '永恒' + if (!d.includes('4')) return '无4' + return '通货' + } + // 根据 URL 参数确定默认标签 const params = new URLSearchParams(window.location.hash.split('?')[1] || '') const modeParam = params.get('mode') const defaultTab = modeParam === 'manual' ? 'manual' : 'ai' - const [activeTab, setActiveTab] = useState(defaultTab) // ai, manual, batch + const [activeTab, setActiveTab] = useState(defaultTab) // ai, manual, deal const [form, setForm] = useState(getDefaultForm()) const [saving, setSaving] = useState(false) const [error, setError] = useState('') @@ -405,15 +441,15 @@ export default function Add() {
-
@@ -435,7 +471,7 @@ export default function Add() { style={{ padding: '12px 24px', background: 'rgba(239, 68, 68, 0.2)', color: '#ef4444', border: '1px solid #ef4444', borderRadius: '8px', fontSize: '14px', cursor: 'pointer' }}>🗑️ 重新选择 @@ -657,16 +693,137 @@ export default function Add() { )} - {/* 批量录入模式 */} - {activeTab === 'batch' && ( -
-
🚧
-
批量录入开发中
-
敬请期待后续版本
-
- )} + {/* 版本号 */} + + {/* 行情录入模式 */} + {activeTab === 'deal' && ( +
+
+
成交行情录入
+ +
+
冠字号 *
+ setDealForm({...dealForm, serial: e.target.value.toUpperCase(), category: autoCategory(e.target.value)})} + placeholder="J0xxxxxxxx" + style={{ width: '100%', padding: '12px', background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '8px', color: '#fff', fontSize: '15px' }} /> +
+ + {dealForm.category && ( +
+
号码分类
+
{dealForm.category}
+
+ )} + +
+
包装类型
+
+ {['单张', '标十', '标百'].map(p => ( + + ))} +
+
+ +
+
成交价格 *
+ setDealForm({...dealForm, price: e.target.value})} + placeholder="请输入成交价格" + style={{ width: '100%', padding: '12px', background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '8px', color: '#fff', fontSize: '15px' }} /> +
+ +
+
成交平台 *
+ +
+ +
+
+
出售者
+ setDealForm({...dealForm, seller: e.target.value})} + placeholder="请输入出售者" + style={{ width: '100%', padding: '12px', background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '8px', color: '#fff', fontSize: '15px' }} /> +
+
+
购买者
+ setDealForm({...dealForm, buyer: e.target.value})} + placeholder="请输入购买者" + style={{ width: '100%', padding: '12px', background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '8px', color: '#fff', fontSize: '15px' }} /> +
+
+ +
+
成交日期 *
+ setDealForm({...dealForm, date: e.target.value})} + style={{ width: '100%', padding: '12px', background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '8px', color: '#fff', fontSize: '15px' }} /> +
+ + +
+
+ )} +
v{APP_VERSION}
) diff --git a/frontend/src/pages/Home.jsx b/frontend/src/pages/Home.jsx index 6c38011..1476144 100644 --- a/frontend/src/pages/Home.jsx +++ b/frontend/src/pages/Home.jsx @@ -367,13 +367,13 @@ export default function Home() { {(isAdmin ? [ { icon: '🏠', label: '首页', hash: '#/', idx: 0 }, { icon: '📚', label: '藏品', hash: '#/list', idx: 1 }, - { icon: '➕', label: '添加', hash: '#/ocr', idx: 2 }, + { icon: '➕', label: '录入', hash: '#/ocr', idx: 2 }, { icon: '📊', label: '统计', hash: '#/stats', idx: 3 }, { icon: '⚙️', label: '管理', hash: '#/admin', idx: 4 } ] : [ { icon: '🏠', label: '首页', hash: '#/', idx: 0 }, { icon: '📚', label: '藏品', hash: '#/list', idx: 1 }, - { icon: '➕', label: '添加', hash: '#/ocr', idx: 2 }, + { icon: '➕', label: '录入', hash: '#/ocr', idx: 2 }, { icon: '📊', label: '统计', hash: '#/stats', idx: 3 } ]).map((item) => (
window.location.hash = item.hash} style={{ diff --git a/frontend/src/pages/Info.jsx b/frontend/src/pages/Info.jsx deleted file mode 100644 index 17c7baf..0000000 --- a/frontend/src/pages/Info.jsx +++ /dev/null @@ -1,680 +0,0 @@ -import React, { useState, useEffect } from 'react' - -// 信息页面 - 寻配号发布和发布管理(包含寻号/行情区分) -export default function Info() { - // 检查登录状态,未登录则跳转到登录页 - if (!localStorage.getItem('token')) { - window.location.hash = '#/login' - return null - } - - // 顶部tab:寻配号发布 / 发布管理 - const [activeTab, setActiveTab] = useState('manage') - const [showPublish, setShowPublish] = useState(false) - const [publishType, setPublishType] = useState('deal') // 'seek' 或 'deal' - const [myList, setMyList] = useState([]) - const [loading, setLoading] = useState(false) - const [editingItem, setEditingItem] = useState(null) - const [filterType, setFilterType] = useState('all') // all/seek/deal - const [expandedItems, setExpandedItems] = useState({}) // 展开状态 - - const [formData, setFormData] = useState({ - edition: '龙钞', type: '标百', isGraded: true, category: '成交', source: '直播', title: '' - }) - - // 寻配号发布表单 - const [seekForm, setSeekForm] = useState({ - edition: '龙钞', price: '', features: '', contact: '', content: '', title: '' - }) - - const API_BASE = localStorage.getItem('API_BASE') || '' - - // 切换展开/收起 - const toggleExpand = (itemId) => { - setExpandedItems(prev => ({ - ...prev, - [itemId]: !prev[itemId] - })) - } - - useEffect(() => { - if (activeTab === 'manage') fetchMyList() - }, [activeTab]) - - useEffect(() => { - // 自动生成行情标题 - const now = new Date() - const date = `${now.getFullYear()}/${now.getMonth()+1}/${now.getDate()}` - const grade = formData.isGraded ? '(评级币)' : '(裸钞)' - const title = `「${date} ${formData.edition} ${formData.type} ${formData.category} 行情信息${grade}」` - setFormData(prev => ({ ...prev, title })) - }, [formData.edition, formData.type, formData.isGraded, formData.category]) - - useEffect(() => { - // 自动生成寻配号标题 - if (seekForm.edition || seekForm.features) { - const title = `「寻号 ${seekForm.edition} J0${seekForm.features || 'XXXXXXXX'}」` - setSeekForm(prev => ({ ...prev, title })) - } - }, [seekForm.edition, seekForm.features]) - - const fetchMyList = async () => { - setLoading(true) - try { - const token = localStorage.getItem('token') - const res = await fetch(`${API_BASE}/api/information/list`, { - headers: { Authorization: `Bearer ${token}` } - }) - if (res.ok) { - const data = await res.json() - setMyList(data || []) - } - } catch (e) { - console.error(e) - } - setLoading(false) - } - - // 发布寻配号 - const handlePublishSeek = async () => { - if (!seekForm.contact) { - alert('请填写联系方式') - return - } - try { - const token = localStorage.getItem('token') - const res = await fetch(`${API_BASE}/api/information/`, { - method: 'POST', - headers: { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - title: seekForm.title, - content: document.getElementById('seekContent')?.value || seekForm.content, - info_type: 'seek', - expect_category: seekForm.edition, - expect_number: seekForm.features ? `J0${seekForm.features}` : null - }) - }) - const data = await res.json() - if (data.id || data.code === 0) { - alert('发布成功!') - setShowPublish(false) - setSeekForm({ edition: '龙钞', price: '', features: '', contact: '', content: '', title: '' }) - const contentEl = document.getElementById('seekContent') - if (contentEl) contentEl.value = '' - fetchMyList() - } else { - alert(data.message || '发布失败') - } - } catch (e) { - alert('发布失败: ' + e.message) - } - } - - // 发布行情(新增发布默认是行情) - const handlePublishDeal = async () => { - const content = document.getElementById('publishContent')?.value || '' - if (!formData.title) { - alert('请填写标题') - return - } - try { - const token = localStorage.getItem('token') - const res = await fetch(`${API_BASE}/api/information/`, { - method: 'POST', - headers: { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - title: formData.title, - content: content, - info_type: formData.category === '成交' ? 'deal' : 'seek' - }) - }) - const data = await res.json() - if (data.id || data.code === 0) { - alert('发布成功!') - setShowPublish(false) - setFormData({ edition: '龙钞', type: '标百', isGraded: true, category: '成交', source: '直播', title: '' }) - const contentEl = document.getElementById('publishContent') - if (contentEl) contentEl.value = '' - fetchMyList() - } else { - alert(data.message || '发布失败') - } - } catch (e) { - alert('发布失败: ' + e.message) - } - } - - // 删除 - const handleDelete = async (id) => { - if (!confirm('确定删除这条信息吗?')) return - try { - const token = localStorage.getItem('token') - const res = await fetch(`${API_BASE}/api/information/${id}`, { - method: 'DELETE', - headers: { Authorization: `Bearer ${token}` } - }) - if (res.ok) { - alert('删除成功') - fetchMyList() - } else { - alert('删除失败') - } - } catch (e) { - alert('删除失败: ' + e.message) - } - } - - // 编辑 - const handleEdit = (item) => { - setEditingItem({ id: item.id, title: item.title, content: item.content }) - } - - const saveEdit = async () => { - if (!editingItem) return - try { - const token = localStorage.getItem('token') - const res = await fetch(`${API_BASE}/api/information/${editingItem.id}`, { - method: 'PUT', - headers: { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ title: editingItem.title, content: editingItem.content }) - }) - if (res.ok) { - alert('保存成功') - setEditingItem(null) - fetchMyList() - } else { - alert('保存失败') - } - } catch (e) { - alert('保存失败: ' + e.message) - } - } - - const getUserPhone = () => { - try { - const user = JSON.parse(localStorage.getItem('user') || '{}') - return user.phone || user.phoneNumber || user.mobile || user.tel || '' - } catch { - return '' - } - } - - useEffect(() => { - setSeekForm(prev => ({ ...prev, contact: getUserPhone() })) - }, []) - - // 格式化日期 - const formatDate = (date) => { - if (!date) return '-' - return new Date(date).toLocaleString('zh-CN').slice(0, 16) - } - - // 过滤后的列表 - const filteredList = myList.filter(item => filterType === 'all' || item.info_type === filterType) - - return ( -
- {/* 顶部 Tab 切换 */} -
-
- {[ - { key: 'manage', label: '📋 发布管理' } - ].map(tab => ( -
setActiveTab(tab.key)} - style={{ - flex: 1, - padding: '12px 16px', - borderRadius: '10px', - background: activeTab === tab.key ? 'linear-gradient(135deg, #3b82f6 0%, #2563eb 100%)' : 'transparent', - color: activeTab === tab.key ? '#fff' : '#9ca3af', - cursor: 'pointer', - textAlign: 'center', - fontSize: '14px', - fontWeight: '600', - transition: 'all 0.3s' - }} - > - {tab.label} -
- ))} -
-
- -
- {/* 寻配号发布页 - 已删除 */} - {false && ( -
-

🔍 寻配号发布

- -
-
- -
- {['龙钞', '马钞', '蛇钞'].map(ed => { - const selected = seekForm.edition === ed - return ( - - ) - })} -
-
- -
- - setSeekForm({ ...seekForm, features: e.target.value.toUpperCase().slice(0, 8) })} - placeholder="输入号码特征,如:12345678" - maxLength={8} - style={{ width: '100%', padding: '12px', background: '#1f2937', border: '1px solid #374151', borderRadius: '10px', color: '#fff', fontSize: '14px', boxSizing: 'border-box' }} - /> -
X=任意数字 A=非4 B=非47 C=非347 D=非247
-
- -
- - -
- -
- -