From 0e7b5cbfdfad456866f19fb50489c2cbc61b5636 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B2=E8=BE=B0=E7=94=9F=E4=BA=A7?= Date: Wed, 25 Mar 2026 10:58:00 +0800 Subject: [PATCH] =?UTF-8?q?v1.2.14:=20=E6=96=B0=E5=A2=9E=E8=B5=84=E8=AE=AF?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E9=A1=B5=E9=9D=A2=EF=BC=88=E5=AF=BB=E9=85=8D?= =?UTF-8?q?=E5=8F=B7/=E6=88=90=E4=BA=A4=E6=95=B0=E6=8D=AE/=E5=8F=91?= =?UTF-8?q?=E5=B8=83=E4=B8=AD=E5=BF=83=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- VERSION | 2 +- backend/app/main.py | 2 + backend/app/models/models.py | 61 ++- backend/app/routers/information.py | 496 ++++++++++++++++++++ config/VERSION | 2 +- frontend/index.html | 2 +- frontend/package-lock.json | 4 +- frontend/src/App.jsx | 3 + frontend/src/pages/News.jsx | 700 +++++++++++++++++++++++++++++ 9 files changed, 1266 insertions(+), 6 deletions(-) create mode 100644 backend/app/routers/information.py create mode 100644 frontend/src/pages/News.jsx diff --git a/VERSION b/VERSION index 0b1f1ed..f9d405b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.2.13 +VERSION=1.2.14 diff --git a/backend/app/main.py b/backend/app/main.py index 3dc746d..7624c39 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -15,6 +15,7 @@ from app.middleware.logging import logging_middleware from app.routers import auth, collections, operations from app.routers import ocr as ocr_router from app.routers import users as users_router +from app.routers import information as information_router # 版本信息 - 从 config/VERSION 文件读取 def get_version(): @@ -79,6 +80,7 @@ app.include_router(operations.router) app.include_router(ocr_router.router) # OCR 识别 app.include_router(users_router.router) # 当前用户接口 app.include_router(users_router.admin_router) # 管理员用户管理 +app.include_router(information_router.router) # 资讯 @app.get("/") diff --git a/backend/app/models/models.py b/backend/app/models/models.py index f010629..1794bb8 100644 --- a/backend/app/models/models.py +++ b/backend/app/models/models.py @@ -1,5 +1,5 @@ # 数据库模型 - 使用字段编码 -from sqlalchemy import Column, String, Float, Boolean, DateTime, Integer, Text, ForeignKey, Date +from sqlalchemy import Column, String, Float, Boolean, DateTime, Integer, Text, ForeignKey, Date, UniqueConstraint from sqlalchemy.orm import relationship from sqlalchemy.sql import func from app.core.database import Base @@ -146,3 +146,62 @@ class CustomField(Base): created_at = Column(DateTime(timezone=True), server_default=func.now()) user = relationship("User", back_populates="custom_fields") + + +# 资讯模型 +class Information(Base): + __tablename__ = "information" + + id = Column(String(36), primary_key=True, default=generate_uuid) + user_id = Column(String(36), ForeignKey("users.f99_90_id", ondelete="CASCADE"), nullable=False, index=True) + + # 信息类型: seek-寻配号, deal-成交数据, publish-发布 + info_type = Column(String(20), nullable=False, index=True) + + # 标题 + title = Column(String(255), nullable=False) + + # 内容描述 + content = Column(Text, nullable=True) + + # 关联藏品ID + collection_id = Column(String(36), ForeignKey("collections.f99_90_id", ondelete="SET NULL"), nullable=True) + + # 期望条件 (寻配号用) + expect_category = Column(String(100), nullable=True) # 期望类别 + expect_version = Column(String(100), nullable=True) # 期望版别 + expect_packaging = Column(String(100), nullable=True) # 期望包装 + expect_number = Column(String(50), nullable=True) # 期望号码 + expect_price_min = Column(Float, nullable=True) # 期望价格区间 + expect_price_max = Column(Float, nullable=True) + + # 成交价格 (成交数据用) + deal_price = Column(Float, nullable=True) + deal_date = Column(Date, nullable=True) + + # 状态: active-有效, closed-已关闭, expired-已过期 + status = Column(String(20), default="active", index=True) + + # 浏览/联系次数 + view_count = Column(Integer, default=0) + contact_count = Column(Integer, default=0) + + created_at = Column(DateTime(timezone=True), server_default=func.now(), index=True) + updated_at = Column(DateTime(timezone=True), onupdate=func.now()) + + user = relationship("User") + collection = relationship("Collection") + + +# 资讯关联用户 (收藏/点赞) +class InformationLike(Base): + __tablename__ = "information_likes" + + id = Column(String(36), primary_key=True, default=generate_uuid) + information_id = Column(String(36), ForeignKey("information.id", ondelete="CASCADE"), nullable=False, index=True) + user_id = Column(String(36), ForeignKey("users.f99_90_id", ondelete="CASCADE"), nullable=False, index=True) + created_at = Column(DateTime(timezone=True), server_default=func.now()) + + __table_args__ = ( + UniqueConstraint('information_id', 'user_id', name='uq_information_user'), + ) diff --git a/backend/app/routers/information.py b/backend/app/routers/information.py new file mode 100644 index 0000000..ea7771b --- /dev/null +++ b/backend/app/routers/information.py @@ -0,0 +1,496 @@ +# 资讯API路由 +from fastapi import APIRouter, Depends, HTTPException, Query +from sqlalchemy.orm import Session, joinedload +from typing import List, Optional +from pydantic import BaseModel +from datetime import datetime, date + +from app.core.database import get_db +from app.core.auth import get_current_user +from app.models.models import User, Information, Collection + +router = APIRouter(prefix="/api/information", tags=["资讯"]) + + +# Schema +class InformationCreate(BaseModel): + info_type: str # seek-寻配号, deal-成交数据, publish-发布 + title: str + content: Optional[str] = None + collection_id: Optional[str] = None + expect_category: Optional[str] = None + expect_version: Optional[str] = None + expect_packaging: Optional[str] = None + expect_number: Optional[str] = None + expect_price_min: Optional[float] = None + expect_price_max: Optional[float] = None + deal_price: Optional[float] = None + deal_date: Optional[date] = None + + +class InformationUpdate(BaseModel): + title: Optional[str] = None + content: Optional[str] = None + status: Optional[str] = None + expect_category: Optional[str] = None + expect_version: Optional[str] = None + expect_packaging: Optional[str] = None + expect_number: Optional[str] = None + expect_price_min: Optional[float] = None + expect_price_max: Optional[float] = None + deal_price: Optional[float] = None + deal_date: Optional[date] = None + + +class InformationResponse(BaseModel): + id: str + user_id: str + info_type: str + title: str + content: Optional[str] + collection_id: Optional[str] + expect_category: Optional[str] + expect_version: Optional[str] + expect_packaging: Optional[str] + expect_number: Optional[str] + expect_price_min: Optional[float] + expect_price_max: Optional[float] + deal_price: Optional[float] + deal_date: Optional[date] + status: str + view_count: int + contact_count: int + created_at: datetime + # 用户信息 + user_name: Optional[str] = None + user_avatar: Optional[str] = None + # 关联藏品信息 + collection_name: Optional[str] = None + collection_category: Optional[str] = None + collection_version: Optional[str] = None + collection_number: Optional[str] = None + + class Config: + from_attributes = True + + +# 资讯列表 +@router.get("/list", response_model=List[InformationResponse]) +def get_information_list( + info_type: Optional[str] = Query(None, description="类型: seek/deal/publish"), + status: str = Query("active", description="状态: active/closed/expired"), + page: int = Query(1, ge=1), + page_size: int = Query(20, ge=1, le=100), + db: Session = Depends(get_db) +): + """获取资讯列表""" + query = db.query(Information).options( + joinedload(Information.user), + joinedload(Information.collection) + ).filter(Information.status == status) + + if info_type: + query = query.filter(Information.info_type == info_type) + + # 按创建时间倒序 + query = query.order_by(Information.created_at.desc()) + + # 分页 + offset = (page - 1) * page_size + items = query.offset(offset).limit(page_size).all() + + # 转换结果 + result = [] + for item in items: + result.append(InformationResponse( + id=item.id, + user_id=item.user_id, + info_type=item.info_type, + title=item.title, + content=item.content, + collection_id=item.collection_id, + expect_category=item.expect_category, + expect_version=item.expect_version, + expect_packaging=item.expect_packaging, + expect_number=item.expect_number, + expect_price_min=item.expect_price_min, + expect_price_max=item.expect_price_max, + deal_price=item.deal_price, + deal_date=item.deal_date, + status=item.status, + view_count=item.view_count, + contact_count=item.contact_count, + created_at=item.created_at, + user_name=item.user.f01_01_name if item.user else None, + user_avatar=item.user.avatar if item.user else None, + collection_name=item.collection.f01_01_name if item.collection else None, + collection_category=item.collection.f01_03_category if item.collection else None, + collection_version=item.collection.f02_11_version if item.collection else None, + collection_number=item.collection.f02_10_prefix_serial if item.collection else None, + )) + + return result + + +# 获取单条资讯 +@router.get("/{info_id}", response_model=InformationResponse) +def get_information( + info_id: str, + db: Session = Depends(get_db) +): + """获取资讯详情""" + item = db.query(Information).options( + joinedload(Information.user), + joinedload(Information.collection) + ).filter(Information.id == info_id).first() + + if not item: + raise HTTPException(status_code=404, detail="资讯不存在") + + # 增加浏览次数 + item.view_count += 1 + db.commit() + + return InformationResponse( + id=item.id, + user_id=item.user_id, + info_type=item.info_type, + title=item.title, + content=item.content, + collection_id=item.collection_id, + expect_category=item.expect_category, + expect_version=item.expect_version, + expect_packaging=item.expect_packaging, + expect_number=item.expect_number, + expect_price_min=item.expect_price_min, + expect_price_max=item.expect_price_max, + deal_price=item.deal_price, + deal_date=item.deal_date, + status=item.status, + view_count=item.view_count, + contact_count=item.contact_count, + created_at=item.created_at, + user_name=item.user.f01_01_name if item.user else None, + user_avatar=item.user.avatar if item.user else None, + collection_name=item.collection.f01_01_name if item.collection else None, + collection_category=item.collection.f01_03_category if item.collection else None, + collection_version=item.collection.f02_11_version if item.collection else None, + collection_number=item.collection.f02_10_prefix_serial if item.collection else None, + ) + + +# 发布资讯 +@router.post("/", response_model=InformationResponse) +def create_information( + data: InformationCreate, + current_user: User = Depends(get_current_user), + db: Session = Depends(get_db) +): + """发布资讯""" + info = Information( + user_id=current_user.f99_90_id, + info_type=data.info_type, + title=data.title, + content=data.content, + collection_id=data.collection_id, + expect_category=data.expect_category, + expect_version=data.expect_version, + expect_packaging=data.expect_packaging, + expect_number=data.expect_number, + expect_price_min=data.expect_price_min, + expect_price_max=data.expect_price_max, + deal_price=data.deal_price, + deal_date=data.deal_date, + status="active" + ) + db.add(info) + db.commit() + db.refresh(info) + + return InformationResponse( + id=info.id, + user_id=info.user_id, + info_type=info.info_type, + title=info.title, + content=info.content, + collection_id=info.collection_id, + expect_category=info.expect_category, + expect_version=info.expect_version, + expect_packaging=info.expect_packaging, + expect_number=info.expect_number, + expect_price_min=info.expect_price_min, + expect_price_max=info.expect_price_max, + deal_price=info.deal_price, + deal_date=info.deal_date, + status=info.status, + view_count=info.view_count, + contact_count=info.contact_count, + created_at=info.created_at, + user_name=current_user.f01_01_name, + user_avatar=current_user.avatar, + collection_name=None, + collection_category=None, + collection_version=None, + collection_number=None, + ) + + +# 更新资讯 +@router.put("/{info_id}", response_model=InformationResponse) +def update_information( + info_id: str, + data: InformationUpdate, + current_user: User = Depends(get_current_user), + db: Session = Depends(get_db) +): + """更新资讯""" + info = db.query(Information).filter( + Information.id == info_id, + Information.user_id == current_user.f99_90_id + ).first() + + if not info: + raise HTTPException(status_code=404, detail="资讯不存在或无权修改") + + # 更新字段 + if data.title is not None: + info.title = data.title + if data.content is not None: + info.content = data.content + if data.status is not None: + info.status = data.status + if data.expect_category is not None: + info.expect_category = data.expect_category + if data.expect_version is not None: + info.expect_version = data.expect_version + if data.expect_packaging is not None: + info.expect_packaging = data.expect_packaging + if data.expect_number is not None: + info.expect_number = data.expect_number + if data.expect_price_min is not None: + info.expect_price_min = data.expect_price_min + if data.expect_price_max is not None: + info.expect_price_max = data.expect_price_max + if data.deal_price is not None: + info.deal_price = data.deal_price + if data.deal_date is not None: + info.deal_date = data.deal_date + + db.commit() + db.refresh(info) + + return InformationResponse( + id=info.id, + user_id=info.user_id, + info_type=info.info_type, + title=info.title, + content=info.content, + collection_id=info.collection_id, + expect_category=info.expect_category, + expect_version=info.expect_version, + expect_packaging=info.expect_packaging, + expect_number=info.expect_number, + expect_price_min=info.expect_price_min, + expect_price_max=info.expect_price_max, + deal_price=info.deal_price, + deal_date=info.deal_date, + status=info.status, + view_count=info.view_count, + contact_count=info.contact_count, + created_at=info.created_at, + user_name=current_user.f01_01_name, + user_avatar=current_user.avatar, + collection_name=info.collection.f01_01_name if info.collection else None, + collection_category=info.collection.f01_03_category if info.collection else None, + collection_version=info.collection.f02_11_version if info.collection else None, + collection_number=info.collection.f02_10_prefix_serial if info.collection else None, + ) + + +# 删除资讯 +@router.delete("/{info_id}") +def delete_information( + info_id: str, + current_user: User = Depends(get_current_user), + db: Session = Depends(get_db) +): + """删除资讯""" + info = db.query(Information).filter( + Information.id == info_id, + Information.user_id == current_user.f99_90_id + ).first() + + if not info: + raise HTTPException(status_code=404, detail="资讯不存在或无权删除") + + db.delete(info) + db.commit() + + return {"message": "删除成功"} + + +# 寻配号 - 自动匹配推荐藏品 +@router.get("/seek/match") +def get_seek_match( + info_id: str, + current_user: User = Depends(get_current_user), + db: Session = Depends(get_db) +): + """获取符合条件的我的藏品推荐""" + info = db.query(Information).filter( + Information.id == info_id, + Information.info_type == "seek" + ).first() + + if not info: + raise HTTPException(status_code=404, detail="寻配号信息不存在") + + # 查询当前用户的藏品,匹配条件 + query = db.query(Collection).filter( + Collection.f99_91_user_id == current_user.f99_90_id, + Collection.f01_04_status == "in_collection" + ) + + if info.expect_category: + query = query.filter(Collection.f01_03_category == info.expect_category) + if info.expect_version: + query = query.filter(Collection.f02_11_version == info.expect_version) + if info.expect_packaging: + query = query.filter(Collection.f02_12_packaging == info.expect_packaging) + if info.expect_number: + query = query.filter(Collection.f02_10_prefix_serial.contains(info.expect_number)) + if info.expect_price_min: + query = query.filter(Collection.f05_40_cost_price >= info.expect_price_min) + if info.expect_price_max: + query = query.filter(Collection.f05_40_cost_price <= info.expect_price_max) + + matched_collections = query.all() + + return { + "info_id": info_id, + "matched_count": len(matched_collections), + "collections": [ + { + "id": c.f99_90_id, + "name": c.f01_01_name, + "category": c.f01_03_category, + "version": c.f02_11_version, + "packaging": c.f02_12_packaging, + "number": c.f02_10_prefix_serial, + "cost_price": c.f05_40_cost_price, + } + for c in matched_collections + ] + } + + +# 成交数据统计 +@router.get("/deal/stats") +def get_deal_stats( + days: int = Query(7, ge=1, le=90, description="统计天数"), + db: Session = Depends(get_db) +): + """获取成交数据统计""" + from sqlalchemy import func + from datetime import timedelta + + start_date = datetime.now() - timedelta(days=days) + + # 按版别统计 + by_version = db.query( + Information.expect_version, + func.count(Information.id).label("count"), + func.avg(Information.deal_price).label("avg_price"), + func.max(Information.deal_price).label("max_price"), + func.min(Information.deal_price).label("min_price") + ).filter( + Information.info_type == "deal", + Information.status == "active", + Information.created_at >= start_date + ).group_by(Information.expect_version).all() + + # 按包装统计 + by_packaging = db.query( + Information.expect_packaging, + func.count(Information.id).label("count"), + func.avg(Information.deal_price).label("avg_price") + ).filter( + Information.info_type == "deal", + Information.status == "active", + Information.created_at >= start_date + ).group_by(Information.expect_packaging).all() + + # 按号码分类统计 + by_number = db.query( + Information.expect_number, + func.count(Information.id).label("count"), + func.avg(Information.deal_price).label("avg_price") + ).filter( + Information.info_type == "deal", + Information.status == "active", + Information.expect_number.isnot(None), + Information.created_at >= start_date + ).group_by(Information.expect_number).all() + + return { + "days": days, + "by_version": [ + {"version": r[0] or "未知", "count": r[1], "avg_price": float(r[2] or 0), "max_price": float(r[3] or 0), "min_price": float(r[4] or 0)} + for r in by_version if r[0] + ], + "by_packaging": [ + {"packaging": r[0] or "未知", "count": r[1], "avg_price": float(r[2] or 0)} + for r in by_packaging if r[0] + ], + "by_number": [ + {"number": r[0], "count": r[1], "avg_price": float(r[2] or 0)} + for r in by_number + ] + } + + +# 获取我的发布列表 +@router.get("/my/list", response_model=List[InformationResponse]) +def get_my_information_list( + page: int = Query(1, ge=1), + page_size: int = Query(20, ge=1, le=100), + current_user: User = Depends(get_current_user), + db: Session = Depends(get_db) +): + """获取我的发布列表""" + items = db.query(Information).options( + joinedload(Information.collection) + ).filter( + Information.user_id == current_user.f99_90_id + ).order_by(Information.created_at.desc()).offset((page-1)*page_size).limit(page_size).all() + + result = [] + for item in items: + result.append(InformationResponse( + id=item.id, + user_id=item.user_id, + info_type=item.info_type, + title=item.title, + content=item.content, + collection_id=item.collection_id, + expect_category=item.expect_category, + expect_version=item.expect_version, + expect_packaging=item.expect_packaging, + expect_number=item.expect_number, + expect_price_min=item.expect_price_min, + expect_price_max=item.expect_price_max, + deal_price=item.deal_price, + deal_date=item.deal_date, + status=item.status, + view_count=item.view_count, + contact_count=item.contact_count, + created_at=item.created_at, + user_name=current_user.f01_01_name, + user_avatar=current_user.avatar, + collection_name=item.collection.f01_01_name if item.collection else None, + collection_category=item.collection.f01_03_category if item.collection else None, + collection_version=item.collection.f02_11_version if item.collection else None, + collection_number=item.collection.f02_10_prefix_serial if item.collection else None, + )) + + return result diff --git a/config/VERSION b/config/VERSION index 0b1f1ed..f9d405b 100644 --- a/config/VERSION +++ b/config/VERSION @@ -1 +1 @@ -1.2.13 +VERSION=1.2.14 diff --git a/frontend/index.html b/frontend/index.html index 3b9b15b..569b6ed 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -4,7 +4,7 @@ - 甲辰收藏 v1.2.11 + 甲辰收藏 v0.0.0 diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 98bd8cb..156d83c 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "jiachenlong-frontend", - "version": "1.0.1", + "version": "1.2.12", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "jiachenlong-frontend", - "version": "1.0.1", + "version": "1.2.12", "dependencies": { "axios": "^1.7.9", "react": "^18.3.1", diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 375b78d..4c37794 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -4,6 +4,7 @@ import Settings from './pages/Settings' import List from './pages/List' import Add from './pages/Add' import Stats from './pages/Stats' +import News from './pages/News' import Login from './pages/Login' import Detail from './pages/Detail' import Edit from './pages/Edit' @@ -28,6 +29,7 @@ export default function App() { const getComponent = () => { const basePath = path.split('?')[0] if (basePath === '/') return + if (basePath === '/news') return if (basePath === '/stats') return if (basePath === '/list') return if (basePath === '/add') return @@ -82,6 +84,7 @@ export default function App() { }}> {[ { path: '/', icon: '🏠', label: '首页' }, + { path: '/news', icon: '📰', label: '资讯' }, { path: '/stats', icon: '📊', label: '统计' }, { path: '/list', icon: '📚', label: '藏品' }, { path: '/add', icon: '🎯', label: '添加' }, diff --git a/frontend/src/pages/News.jsx b/frontend/src/pages/News.jsx new file mode 100644 index 0000000..4cc319b --- /dev/null +++ b/frontend/src/pages/News.jsx @@ -0,0 +1,700 @@ +import React, { useState, useEffect } from 'react' + +// 资讯页面 - 寻配号、成交数据、发布中心 +export default function News() { + const [activeTab, setActiveTab] = useState('seek') // seek-寻配号, deal-成交, publish-发布 + const [infoList, setInfoList] = useState([]) + const [loading, setLoading] = useState(false) + const [showPublish, setShowPublish] = useState(false) + const [myList, setMyList] = useState([]) + + // 发布表单 + const [formData, setFormData] = useState({ + info_type: 'seek', + title: '', + content: '', + collection_id: '', + expect_category: '', + expect_version: '', + expect_packaging: '', + expect_number: '', + expect_price_min: '', + expect_price_max: '', + deal_price: '', + deal_date: '' + }) + + // 我的藏品列表(用于关联) + const [myCollections, setMyCollections] = useState([]) + + const API_BASE = localStorage.getItem('API_BASE') || '' + + useEffect(() => { + fetchInfoList() + fetchMyCollections() + fetchMyList() + }, [activeTab]) + + // 获取资讯列表 + const fetchInfoList = async () => { + setLoading(true) + try { + const token = localStorage.getItem('token') + const res = await fetch(`${API_BASE}/api/information/list?info_type=${activeTab}`, { + headers: { 'Authorization': `Bearer ${token}` } + }) + const data = await res.json() + setInfoList(data || []) + } catch (e) { + console.error(e) + } + setLoading(false) + } + + // 获取我的藏品列表 + const fetchMyCollections = async () => { + try { + const token = localStorage.getItem('token') + const res = await fetch(`${API_BASE}/api/collections?page=1&page_size=100`, { + headers: { 'Authorization': `Bearer ${token}` } + }) + const data = await res.json() + setMyCollections(data.items || []) + } catch (e) { + console.error(e) + } + } + + // 获取我的发布列表 + const fetchMyList = async () => { + try { + const token = localStorage.getItem('token') + const res = await fetch(`${API_BASE}/api/information/my/list`, { + headers: { 'Authorization': `Bearer ${token}` } + }) + const data = await res.json() + setMyList(data || []) + } catch (e) { + console.error(e) + } + } + + // 发布资讯 + const handlePublish = async () => { + 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({ + ...formData, + expect_price_min: formData.expect_price_min ? parseFloat(formData.expect_price_min) : null, + expect_price_max: formData.expect_price_max ? parseFloat(formData.expect_price_max) : null, + deal_price: formData.deal_price ? parseFloat(formData.deal_price) : null, + }) + }) + if (res.ok) { + alert('发布成功!') + setShowPublish(false) + setFormData({ + info_type: 'seek', + title: '', + content: '', + collection_id: '', + expect_category: '', + expect_version: '', + expect_packaging: '', + expect_number: '', + expect_price_min: '', + expect_price_max: '', + deal_price: '', + deal_date: '' + }) + fetchInfoList() + fetchMyList() + } + } catch (e) { + alert('发布失败') + } + } + + // 删除发布 + const handleDelete = async (id) => { + if (!confirm('确定删除?')) return + try { + const token = localStorage.getItem('token') + await fetch(`${API_BASE}/api/information/${id}`, { + method: 'DELETE', + headers: { 'Authorization': `Bearer ${token}` } + }) + fetchMyList() + } catch (e) { + console.error(e) + } + } + + // 格式化日期 + const formatDate = (dateStr) => { + if (!dateStr) return '' + return new Date(dateStr).toLocaleDateString('zh-CN') + } + + // 资讯类型映射 + const typeMap = { + seek: '寻配号', + deal: '成交数据', + publish: '发布' + } + + return ( +
+ {/* 顶部标题 */} +
+

📰 资讯中心

+
+ + {/* 二级标签切换 */} +
+ {[ + { key: 'seek', label: '🔍 寻配号' }, + { key: 'deal', label: '📈 成交数据' }, + { key: 'publish', label: '➕ 发布' } + ].map(tab => ( +
setActiveTab(tab.key)} + style={{ + flex: 1, + textAlign: 'center', + padding: '10px', + borderRadius: '6px', + cursor: 'pointer', + background: activeTab === tab.key ? '#3b82f6' : 'transparent', + color: activeTab === tab.key ? '#fff' : '#94a3b8', + fontSize: '14px', + fontWeight: activeTab === tab.key ? 'bold' : 'normal' + }} + > + {tab.label} +
+ ))} +
+ + {/* 发布按钮 */} + {activeTab !== 'publish' && ( +
+ +
+ )} + + {/* 发布表单 */} + {showPublish && ( +
+

发布信息

+ + {/* 选择类型 */} +
+ + +
+ + {/* 标题 */} +
+ + setFormData({...formData, title: e.target.value})} + placeholder="请输入标题" + style={{ + width: '100%', + padding: '10px', + background: '#0f172a', + border: '1px solid #334155', + borderRadius: '6px', + color: '#fff', + marginTop: '4px', + boxSizing: 'border-box' + }} + /> +
+ + {/* 内容 */} +
+ +