v1.2.14: 新增资讯功能页面(寻配号/成交数据/发布中心)
This commit is contained in:
parent
fadb0e5b4f
commit
0e7b5cbfdf
|
|
@ -15,6 +15,7 @@ from app.middleware.logging import logging_middleware
|
||||||
from app.routers import auth, collections, operations
|
from app.routers import auth, collections, operations
|
||||||
from app.routers import ocr as ocr_router
|
from app.routers import ocr as ocr_router
|
||||||
from app.routers import users as users_router
|
from app.routers import users as users_router
|
||||||
|
from app.routers import information as information_router
|
||||||
|
|
||||||
# 版本信息 - 从 config/VERSION 文件读取
|
# 版本信息 - 从 config/VERSION 文件读取
|
||||||
def get_version():
|
def get_version():
|
||||||
|
|
@ -79,6 +80,7 @@ app.include_router(operations.router)
|
||||||
app.include_router(ocr_router.router) # OCR 识别
|
app.include_router(ocr_router.router) # OCR 识别
|
||||||
app.include_router(users_router.router) # 当前用户接口
|
app.include_router(users_router.router) # 当前用户接口
|
||||||
app.include_router(users_router.admin_router) # 管理员用户管理
|
app.include_router(users_router.admin_router) # 管理员用户管理
|
||||||
|
app.include_router(information_router.router) # 资讯
|
||||||
|
|
||||||
|
|
||||||
@app.get("/")
|
@app.get("/")
|
||||||
|
|
|
||||||
|
|
@ -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.orm import relationship
|
||||||
from sqlalchemy.sql import func
|
from sqlalchemy.sql import func
|
||||||
from app.core.database import Base
|
from app.core.database import Base
|
||||||
|
|
@ -146,3 +146,62 @@ class CustomField(Base):
|
||||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||||
|
|
||||||
user = relationship("User", back_populates="custom_fields")
|
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'),
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -1 +1 @@
|
||||||
1.2.13
|
VERSION=1.2.14
|
||||||
|
|
|
||||||
|
|
@ -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.11</title>
|
<title>甲辰收藏 v0.0.0</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,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "jiachenlong-frontend",
|
"name": "jiachenlong-frontend",
|
||||||
"version": "1.0.1",
|
"version": "1.2.12",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "jiachenlong-frontend",
|
"name": "jiachenlong-frontend",
|
||||||
"version": "1.0.1",
|
"version": "1.2.12",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.7.9",
|
"axios": "^1.7.9",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import Settings from './pages/Settings'
|
||||||
import List from './pages/List'
|
import List from './pages/List'
|
||||||
import Add from './pages/Add'
|
import Add from './pages/Add'
|
||||||
import Stats from './pages/Stats'
|
import Stats from './pages/Stats'
|
||||||
|
import News from './pages/News'
|
||||||
import Login from './pages/Login'
|
import Login from './pages/Login'
|
||||||
import Detail from './pages/Detail'
|
import Detail from './pages/Detail'
|
||||||
import Edit from './pages/Edit'
|
import Edit from './pages/Edit'
|
||||||
|
|
@ -28,6 +29,7 @@ export default function App() {
|
||||||
const getComponent = () => {
|
const getComponent = () => {
|
||||||
const basePath = path.split('?')[0]
|
const basePath = path.split('?')[0]
|
||||||
if (basePath === '/') return <Home />
|
if (basePath === '/') return <Home />
|
||||||
|
if (basePath === '/news') return <News />
|
||||||
if (basePath === '/stats') return <Stats />
|
if (basePath === '/stats') return <Stats />
|
||||||
if (basePath === '/list') return <List />
|
if (basePath === '/list') return <List />
|
||||||
if (basePath === '/add') return <Add />
|
if (basePath === '/add') return <Add />
|
||||||
|
|
@ -82,6 +84,7 @@ export default function App() {
|
||||||
}}>
|
}}>
|
||||||
{[
|
{[
|
||||||
{ path: '/', icon: '🏠', label: '首页' },
|
{ path: '/', icon: '🏠', label: '首页' },
|
||||||
|
{ path: '/news', icon: '📰', label: '资讯' },
|
||||||
{ path: '/stats', icon: '📊', label: '统计' },
|
{ path: '/stats', icon: '📊', label: '统计' },
|
||||||
{ path: '/list', icon: '📚', label: '藏品' },
|
{ path: '/list', icon: '📚', label: '藏品' },
|
||||||
{ path: '/add', icon: '🎯', label: '添加' },
|
{ path: '/add', icon: '🎯', label: '添加' },
|
||||||
|
|
|
||||||
|
|
@ -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 (
|
||||||
|
<div style={{ padding: '16px', paddingBottom: '80px', minHeight: '100vh', background: '#0f172a' }}>
|
||||||
|
{/* 顶部标题 */}
|
||||||
|
<div style={{ textAlign: 'center', marginBottom: '16px' }}>
|
||||||
|
<h1 style={{ fontSize: '20px', color: '#fbbf24', margin: 0 }}>📰 资讯中心</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 二级标签切换 */}
|
||||||
|
<div style={{
|
||||||
|
display: 'flex',
|
||||||
|
background: '#1e293b',
|
||||||
|
borderRadius: '8px',
|
||||||
|
padding: '4px',
|
||||||
|
marginBottom: '16px'
|
||||||
|
}}>
|
||||||
|
{[
|
||||||
|
{ key: 'seek', label: '🔍 寻配号' },
|
||||||
|
{ key: 'deal', label: '📈 成交数据' },
|
||||||
|
{ key: 'publish', label: '➕ 发布' }
|
||||||
|
].map(tab => (
|
||||||
|
<div
|
||||||
|
key={tab.key}
|
||||||
|
onClick={() => 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}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 发布按钮 */}
|
||||||
|
{activeTab !== 'publish' && (
|
||||||
|
<div style={{ marginBottom: '16px', textAlign: 'right' }}>
|
||||||
|
<button
|
||||||
|
onClick={() => setShowPublish(!showPublish)}
|
||||||
|
style={{
|
||||||
|
background: '#3b82f6',
|
||||||
|
color: '#fff',
|
||||||
|
border: 'none',
|
||||||
|
padding: '8px 16px',
|
||||||
|
borderRadius: '6px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
fontSize: '14px'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{showPublish ? '取消发布' : '➕ 发布'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 发布表单 */}
|
||||||
|
{showPublish && (
|
||||||
|
<div style={{
|
||||||
|
background: '#1e293b',
|
||||||
|
borderRadius: '12px',
|
||||||
|
padding: '16px',
|
||||||
|
marginBottom: '16px'
|
||||||
|
}}>
|
||||||
|
<h3 style={{ color: '#fbbf24', marginTop: 0 }}>发布信息</h3>
|
||||||
|
|
||||||
|
{/* 选择类型 */}
|
||||||
|
<div style={{ marginBottom: '12px' }}>
|
||||||
|
<label style={{ color: '#94a3b8', fontSize: '12px' }}>类型</label>
|
||||||
|
<select
|
||||||
|
value={formData.info_type}
|
||||||
|
onChange={(e) => setFormData({...formData, info_type: e.target.value})}
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
padding: '10px',
|
||||||
|
background: '#0f172a',
|
||||||
|
border: '1px solid #334155',
|
||||||
|
borderRadius: '6px',
|
||||||
|
color: '#fff',
|
||||||
|
marginTop: '4px'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<option value="seek">🔍 寻配号</option>
|
||||||
|
<option value="deal">📈 成交数据</option>
|
||||||
|
<option value="publish">📝 普通发布</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 标题 */}
|
||||||
|
<div style={{ marginBottom: '12px' }}>
|
||||||
|
<label style={{ color: '#94a3b8', fontSize: '12px' }}>标题 *</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={formData.title}
|
||||||
|
onChange={(e) => 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'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 内容 */}
|
||||||
|
<div style={{ marginBottom: '12px' }}>
|
||||||
|
<label style={{ color: '#94a3b8', fontSize: '12px' }}>详细描述</label>
|
||||||
|
<textarea
|
||||||
|
value={formData.content}
|
||||||
|
onChange={(e) => setFormData({...formData, content: e.target.value})}
|
||||||
|
placeholder="请输入详细描述..."
|
||||||
|
rows={3}
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
padding: '10px',
|
||||||
|
background: '#0f172a',
|
||||||
|
border: '1px solid #334155',
|
||||||
|
borderRadius: '6px',
|
||||||
|
color: '#fff',
|
||||||
|
marginTop: '4px',
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
resize: 'vertical'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 关联藏品 */}
|
||||||
|
{formData.info_type === 'seek' && (
|
||||||
|
<div style={{ marginBottom: '12px' }}>
|
||||||
|
<label style={{ color: '#94a3b8', fontSize: '12px' }}>关联我的藏品(可选)</label>
|
||||||
|
<select
|
||||||
|
value={formData.collection_id}
|
||||||
|
onChange={(e) => setFormData({...formData, collection_id: e.target.value})}
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
padding: '10px',
|
||||||
|
background: '#0f172a',
|
||||||
|
border: '1px solid #334155',
|
||||||
|
borderRadius: '6px',
|
||||||
|
color: '#fff',
|
||||||
|
marginTop: '4px'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<option value="">不关联</option>
|
||||||
|
{myCollections.map(c => (
|
||||||
|
<option key={c.f99_90_id} value={c.f99_90_id}>
|
||||||
|
{c.f01_01_name} - {c.f01_03_category}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 寻配号条件 */}
|
||||||
|
{formData.info_type === 'seek' && (
|
||||||
|
<>
|
||||||
|
<div style={{ display: 'flex', gap: '8px', marginBottom: '12px' }}>
|
||||||
|
<div style={{ flex: 1 }}>
|
||||||
|
<label style={{ color: '#94a3b8', fontSize: '12px' }}>期望类别</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={formData.expect_category}
|
||||||
|
onChange={(e) => setFormData({...formData, expect_category: e.target.value})}
|
||||||
|
placeholder="如:纪念钞"
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
padding: '10px',
|
||||||
|
background: '#0f172a',
|
||||||
|
border: '1px solid #334155',
|
||||||
|
borderRadius: '6px',
|
||||||
|
color: '#fff',
|
||||||
|
marginTop: '4px',
|
||||||
|
boxSizing: 'border-box'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div style={{ flex: 1 }}>
|
||||||
|
<label style={{ color: '#94a3b8', fontSize: '12px' }}>期望版别</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={formData.expect_version}
|
||||||
|
onChange={(e) => setFormData({...formData, expect_version: e.target.value})}
|
||||||
|
placeholder="如:2024版"
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
padding: '10px',
|
||||||
|
background: '#0f172a',
|
||||||
|
border: '1px solid #334155',
|
||||||
|
borderRadius: '6px',
|
||||||
|
color: '#fff',
|
||||||
|
marginTop: '4px',
|
||||||
|
boxSizing: 'border-box'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style={{ display: 'flex', gap: '8px', marginBottom: '12px' }}>
|
||||||
|
<div style={{ flex: 1 }}>
|
||||||
|
<label style={{ color: '#94a3b8', fontSize: '12px' }}>期望包装</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={formData.expect_packaging}
|
||||||
|
onChange={(e) => setFormData({...formData, expect_packaging: e.target.value})}
|
||||||
|
placeholder="如:散钞"
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
padding: '10px',
|
||||||
|
background: '#0f172a',
|
||||||
|
border: '1px solid #334155',
|
||||||
|
borderRadius: '6px',
|
||||||
|
color: '#fff',
|
||||||
|
marginTop: '4px',
|
||||||
|
boxSizing: 'border-box'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div style={{ flex: 1 }}>
|
||||||
|
<label style={{ color: '#94a3b8', fontSize: '12px' }}>期望号码</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={formData.expect_number}
|
||||||
|
onChange={(e) => setFormData({...formData, expect_number: e.target.value})}
|
||||||
|
placeholder="如:888"
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
padding: '10px',
|
||||||
|
background: '#0f172a',
|
||||||
|
border: '1px solid #334155',
|
||||||
|
borderRadius: '6px',
|
||||||
|
color: '#fff',
|
||||||
|
marginTop: '4px',
|
||||||
|
boxSizing: 'border-box'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style={{ display: 'flex', gap: '8px', marginBottom: '12px' }}>
|
||||||
|
<div style={{ flex: 1 }}>
|
||||||
|
<label style={{ color: '#94a3b8', fontSize: '12px' }}>最低价格</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={formData.expect_price_min}
|
||||||
|
onChange={(e) => setFormData({...formData, expect_price_min: e.target.value})}
|
||||||
|
placeholder="0"
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
padding: '10px',
|
||||||
|
background: '#0f172a',
|
||||||
|
border: '1px solid #334155',
|
||||||
|
borderRadius: '6px',
|
||||||
|
color: '#fff',
|
||||||
|
marginTop: '4px',
|
||||||
|
boxSizing: 'border-box'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div style={{ flex: 1 }}>
|
||||||
|
<label style={{ color: '#94a3b8', fontSize: '12px' }}>最高价格</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={formData.expect_price_max}
|
||||||
|
onChange={(e) => setFormData({...formData, expect_price_max: e.target.value})}
|
||||||
|
placeholder="0"
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
padding: '10px',
|
||||||
|
background: '#0f172a',
|
||||||
|
border: '1px solid #334155',
|
||||||
|
borderRadius: '6px',
|
||||||
|
color: '#fff',
|
||||||
|
marginTop: '4px',
|
||||||
|
boxSizing: 'border-box'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 成交数据 */}
|
||||||
|
{formData.info_type === 'deal' && (
|
||||||
|
<div style={{ display: 'flex', gap: '8px', marginBottom: '12px' }}>
|
||||||
|
<div style={{ flex: 1 }}>
|
||||||
|
<label style={{ color: '#94a3b8', fontSize: '12px' }}>成交价格</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={formData.deal_price}
|
||||||
|
onChange={(e) => setFormData({...formData, deal_price: e.target.value})}
|
||||||
|
placeholder="成交金额"
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
padding: '10px',
|
||||||
|
background: '#0f172a',
|
||||||
|
border: '1px solid #334155',
|
||||||
|
borderRadius: '6px',
|
||||||
|
color: '#fff',
|
||||||
|
marginTop: '4px',
|
||||||
|
boxSizing: 'border-box'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div style={{ flex: 1 }}>
|
||||||
|
<label style={{ color: '#94a3b8', fontSize: '12px' }}>版别</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={formData.expect_version}
|
||||||
|
onChange={(e) => setFormData({...formData, expect_version: e.target.value})}
|
||||||
|
placeholder="如:2024版"
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
padding: '10px',
|
||||||
|
background: '#0f172a',
|
||||||
|
border: '1px solid #334155',
|
||||||
|
borderRadius: '6px',
|
||||||
|
color: '#fff',
|
||||||
|
marginTop: '4px',
|
||||||
|
boxSizing: 'border-box'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div style={{ flex: 1 }}>
|
||||||
|
<label style={{ color: '#94a3b8', fontSize: '12px' }}>包装</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={formData.expect_packaging}
|
||||||
|
onChange={(e) => setFormData({...formData, expect_packaging: e.target.value})}
|
||||||
|
placeholder="如:标十"
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
padding: '10px',
|
||||||
|
background: '#0f172a',
|
||||||
|
border: '1px solid #334155',
|
||||||
|
borderRadius: '6px',
|
||||||
|
color: '#fff',
|
||||||
|
marginTop: '4px',
|
||||||
|
boxSizing: 'border-box'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={handlePublish}
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
padding: '12px',
|
||||||
|
background: '#3b82f6',
|
||||||
|
color: '#fff',
|
||||||
|
border: 'none',
|
||||||
|
borderRadius: '6px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
fontSize: '16px',
|
||||||
|
fontWeight: 'bold'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
发布
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 资讯列表 */}
|
||||||
|
{activeTab === 'publish' ? (
|
||||||
|
// 我的发布
|
||||||
|
<div>
|
||||||
|
<h3 style={{ color: '#fbbf24', marginBottom: '12px' }}>我的发布</h3>
|
||||||
|
{loading ? (
|
||||||
|
<div style={{ textAlign: 'center', color: '#94a3b8', padding: '20px' }}>加载中...</div>
|
||||||
|
) : myList.length === 0 ? (
|
||||||
|
<div style={{ textAlign: 'center', color: '#94a3b8', padding: '20px' }}>暂无发布</div>
|
||||||
|
) : (
|
||||||
|
myList.map(item => (
|
||||||
|
<div key={item.id} style={{
|
||||||
|
background: '#1e293b',
|
||||||
|
borderRadius: '12px',
|
||||||
|
padding: '16px',
|
||||||
|
marginBottom: '12px'
|
||||||
|
}}>
|
||||||
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||||
|
<span style={{
|
||||||
|
background: item.info_type === 'seek' ? '#3b82f6' : item.info_type === 'deal' ? '#10b981' : '#f59e0b',
|
||||||
|
color: '#fff',
|
||||||
|
padding: '2px 8px',
|
||||||
|
borderRadius: '4px',
|
||||||
|
fontSize: '12px'
|
||||||
|
}}>
|
||||||
|
{typeMap[item.info_type]}
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
onClick={() => handleDelete(item.id)}
|
||||||
|
style={{
|
||||||
|
background: '#ef4444',
|
||||||
|
color: '#fff',
|
||||||
|
border: 'none',
|
||||||
|
padding: '4px 8px',
|
||||||
|
borderRadius: '4px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
fontSize: '12px'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<h4 style={{ color: '#fff', margin: '8px 0' }}>{item.title}</h4>
|
||||||
|
<p style={{ color: '#94a3b8', fontSize: '13px', margin: 0 }}>{item.content}</p>
|
||||||
|
<div style={{ color: '#64748b', fontSize: '12px', marginTop: '8px' }}>
|
||||||
|
{formatDate(item.created_at)} · 浏览 {item.view_count} · 联系 {item.contact_count}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
// 寻配号 / 成交数据列表
|
||||||
|
<div>
|
||||||
|
{loading ? (
|
||||||
|
<div style={{ textAlign: 'center', color: '#94a3b8', padding: '20px' }}>加载中...</div>
|
||||||
|
) : infoList.length === 0 ? (
|
||||||
|
<div style={{ textAlign: 'center', color: '#94a3b8', padding: '20px' }}>
|
||||||
|
暂无{activeTab === 'seek' ? '寻配号' : '成交数据'}信息
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
infoList.map(item => (
|
||||||
|
<div key={item.id} style={{
|
||||||
|
background: '#1e293b',
|
||||||
|
borderRadius: '12px',
|
||||||
|
padding: '16px',
|
||||||
|
marginBottom: '12px'
|
||||||
|
}}>
|
||||||
|
{/* 用户信息 */}
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', marginBottom: '8px' }}>
|
||||||
|
<div style={{
|
||||||
|
width: '32px',
|
||||||
|
height: '32px',
|
||||||
|
borderRadius: '50%',
|
||||||
|
background: '#3b82f6',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
color: '#fff',
|
||||||
|
fontSize: '14px',
|
||||||
|
marginRight: '8px'
|
||||||
|
}}>
|
||||||
|
{item.user_name ? item.user_name[0] : '?'}
|
||||||
|
</div>
|
||||||
|
<span style={{ color: '#94a3b8', fontSize: '13px' }}>{item.user_name}</span>
|
||||||
|
<span style={{ color: '#64748b', fontSize: '12px', marginLeft: 'auto' }}>
|
||||||
|
{formatDate(item.created_at)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 标题 */}
|
||||||
|
<h4 style={{ color: '#fff', margin: '0 0 8px 0', fontSize: '16px' }}>{item.title}</h4>
|
||||||
|
|
||||||
|
{/* 内容 */}
|
||||||
|
{item.content && (
|
||||||
|
<p style={{ color: '#94a3b8', fontSize: '14px', margin: '0 0 8px 0' }}>{item.content}</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 寻配号条件 */}
|
||||||
|
{item.info_type === 'seek' && (item.expect_category || item.expect_version || item.expect_number) && (
|
||||||
|
<div style={{
|
||||||
|
background: '#0f172a',
|
||||||
|
borderRadius: '8px',
|
||||||
|
padding: '12px',
|
||||||
|
marginBottom: '8px'
|
||||||
|
}}>
|
||||||
|
<div style={{ color: '#fbbf24', fontSize: '12px', marginBottom: '4px' }}>期望条件</div>
|
||||||
|
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '8px' }}>
|
||||||
|
{item.expect_category && (
|
||||||
|
<span style={{ color: '#94a3b8', fontSize: '12px' }}>类别: {item.expect_category}</span>
|
||||||
|
)}
|
||||||
|
{item.expect_version && (
|
||||||
|
<span style={{ color: '#94a3b8', fontSize: '12px' }}>版别: {item.expect_version}</span>
|
||||||
|
)}
|
||||||
|
{item.expect_packaging && (
|
||||||
|
<span style={{ color: '#94a3b8', fontSize: '12px' }}>包装: {item.expect_packaging}</span>
|
||||||
|
)}
|
||||||
|
{item.expect_number && (
|
||||||
|
<span style={{ color: '#94a3b8', fontSize: '12px' }}>号码: {item.expect_number}</span>
|
||||||
|
)}
|
||||||
|
{(item.expect_price_min || item.expect_price_max) && (
|
||||||
|
<span style={{ color: '#94a3b8', fontSize: '12px' }}>
|
||||||
|
价格: {item.expect_price_min || '?'} - {item.expect_price_max || '?'}元
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 成交数据 */}
|
||||||
|
{item.info_type === 'deal' && (
|
||||||
|
<div style={{
|
||||||
|
background: '#0f172a',
|
||||||
|
borderRadius: '8px',
|
||||||
|
padding: '12px',
|
||||||
|
marginBottom: '8px'
|
||||||
|
}}>
|
||||||
|
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
|
<span style={{ color: '#10b981', fontSize: '18px', fontWeight: 'bold' }}>
|
||||||
|
¥{item.deal_price || '-'}
|
||||||
|
</span>
|
||||||
|
<span style={{ color: '#64748b', fontSize: '12px' }}>
|
||||||
|
{item.expect_version} / {item.expect_packaging}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 关联藏品 */}
|
||||||
|
{item.collection_name && (
|
||||||
|
<div style={{
|
||||||
|
background: '#3b82f620',
|
||||||
|
borderRadius: '8px',
|
||||||
|
padding: '8px 12px',
|
||||||
|
marginBottom: '8px',
|
||||||
|
borderLeft: '3px solid #3b82f6'
|
||||||
|
}}>
|
||||||
|
<span style={{ color: '#3b82f6', fontSize: '12px' }}>关联藏品: </span>
|
||||||
|
<span style={{ color: '#fff', fontSize: '13px' }}>{item.collection_name}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 统计 */}
|
||||||
|
<div style={{ display: 'flex', gap: '16px', color: '#64748b', fontSize: '12px' }}>
|
||||||
|
<span>👁 {item.view_count}</span>
|
||||||
|
<span>📞 {item.contact_count}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue