Merge origin/yichen - 合并一尘分支最新代码
This commit is contained in:
commit
e64d07c782
|
|
@ -52,11 +52,10 @@ app = FastAPI(
|
||||||
# 设置全局错误处理器
|
# 设置全局错误处理器
|
||||||
setup_error_handlers(app)
|
setup_error_handlers(app)
|
||||||
|
|
||||||
# CORS 配置 - 生产环境限制域名
|
# CORS 配置
|
||||||
ALLOWED_ORIGINS = os.getenv("ALLOWED_ORIGINS", "http://47.103.29.111,http://120.55.81.21,https://socoolbot.com").split(",")
|
|
||||||
app.add_middleware(
|
app.add_middleware(
|
||||||
CORSMiddleware,
|
CORSMiddleware,
|
||||||
allow_origins=ALLOWED_ORIGINS, # 生产环境限制域名
|
allow_origins=["*"], # 生产环境应该限制域名
|
||||||
allow_credentials=True,
|
allow_credentials=True,
|
||||||
allow_methods=["*"],
|
allow_methods=["*"],
|
||||||
allow_headers=["*"],
|
allow_headers=["*"],
|
||||||
|
|
@ -67,10 +66,10 @@ uploads_dir = "uploads"
|
||||||
os.makedirs(uploads_dir, exist_ok=True)
|
os.makedirs(uploads_dir, exist_ok=True)
|
||||||
app.mount("/uploads", StaticFiles(directory=uploads_dir), name="uploads")
|
app.mount("/uploads", StaticFiles(directory=uploads_dir), name="uploads")
|
||||||
|
|
||||||
# 挂载项目静态资源目录(可选,生产环境建议用 Nginx)
|
# 挂载项目静态资源目录
|
||||||
# static_dir = Path(__file__).parent.parent.parent / "static"
|
static_dir = Path(__file__).parent.parent / "static"
|
||||||
# if static_dir.exists():
|
if static_dir.exists():
|
||||||
# app.mount("/static", StaticFiles(directory=str(static_dir)), name="static")
|
app.mount("/static", StaticFiles(directory=str(static_dir)), name="static")
|
||||||
|
|
||||||
# 添加日志中间件
|
# 添加日志中间件
|
||||||
app.middleware("http")(logging_middleware)
|
app.middleware("http")(logging_middleware)
|
||||||
|
|
@ -82,7 +81,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.include_router(information_router.router)
|
||||||
app.include_router(yichens_router.router) # 一尘看板
|
app.include_router(yichens_router.router) # 一尘看板
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,14 @@
|
||||||
# 资讯API路由
|
# 资讯API路由
|
||||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||||
from sqlalchemy.orm import Session, joinedload
|
from sqlalchemy.orm import Session, joinedload
|
||||||
|
from sqlalchemy import text
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from datetime import datetime, date
|
from datetime import datetime, date
|
||||||
|
|
||||||
from app.core.database import get_db
|
from app.core.database import get_db
|
||||||
from app.core.auth import get_current_user
|
from app.core.auth import get_current_user
|
||||||
|
from app.core.coolbot_db import coolbot_engine
|
||||||
from app.models.models import User, Information, Collection
|
from app.models.models import User, Information, Collection
|
||||||
|
|
||||||
router = APIRouter(prefix="/api/information", tags=["资讯"])
|
router = APIRouter(prefix="/api/information", tags=["资讯"])
|
||||||
|
|
@ -16,7 +18,7 @@ router = APIRouter(prefix="/api/information", tags=["资讯"])
|
||||||
class InformationCreate(BaseModel):
|
class InformationCreate(BaseModel):
|
||||||
info_type: str # seek-寻配号, deal-成交数据, publish-发布
|
info_type: str # seek-寻配号, deal-成交数据, publish-发布
|
||||||
title: str
|
title: str
|
||||||
content: Optional[str] = None
|
content: Optional[str]
|
||||||
collection_id: Optional[str] = None
|
collection_id: Optional[str] = None
|
||||||
expect_category: Optional[str] = None
|
expect_category: Optional[str] = None
|
||||||
expect_version: Optional[str] = None
|
expect_version: Optional[str] = None
|
||||||
|
|
@ -30,7 +32,7 @@ class InformationCreate(BaseModel):
|
||||||
|
|
||||||
class InformationUpdate(BaseModel):
|
class InformationUpdate(BaseModel):
|
||||||
title: Optional[str] = None
|
title: Optional[str] = None
|
||||||
content: Optional[str] = None
|
content: Optional[str]
|
||||||
status: Optional[str] = None
|
status: Optional[str] = None
|
||||||
expect_category: Optional[str] = None
|
expect_category: Optional[str] = None
|
||||||
expect_version: Optional[str] = None
|
expect_version: Optional[str] = None
|
||||||
|
|
@ -74,6 +76,8 @@ class InformationResponse(BaseModel):
|
||||||
collection_number: Optional[str] = None
|
collection_number: Optional[str] = None
|
||||||
# 匹配数量(我的藏品中满足条件的数量)
|
# 匹配数量(我的藏品中满足条件的数量)
|
||||||
matched_count: Optional[int] = 0
|
matched_count: Optional[int] = 0
|
||||||
|
# 网络数据匹配数量(coolbot_data数据库中满足条件的数量)
|
||||||
|
network_matched_count: Optional[int] = 0
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
from_attributes = True
|
from_attributes = True
|
||||||
|
|
@ -87,7 +91,8 @@ def get_information_list(
|
||||||
page: int = Query(1, ge=1),
|
page: int = Query(1, ge=1),
|
||||||
page_size: int = Query(20, ge=1, le=100),
|
page_size: int = Query(20, ge=1, le=100),
|
||||||
current_user: Optional[User] = Depends(get_current_user),
|
current_user: Optional[User] = Depends(get_current_user),
|
||||||
db: Session = Depends(get_db)
|
db: Session = Depends(get_db),
|
||||||
|
response: Response = None
|
||||||
):
|
):
|
||||||
"""获取资讯列表(公开,无需登录)"""
|
"""获取资讯列表(公开,无需登录)"""
|
||||||
query = db.query(Information).options(
|
query = db.query(Information).options(
|
||||||
|
|
@ -142,8 +147,21 @@ def get_information_list(
|
||||||
collection_version=item.collection.f02_11_version 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,
|
collection_number=item.collection.f02_10_prefix_serial if item.collection else None,
|
||||||
matched_count=matched_count,
|
matched_count=matched_count,
|
||||||
|
network_matched_count=match_collections_count_from_coolbot(item.expect_number) if item.info_type == 'seek' and item.expect_number else 0,
|
||||||
))
|
))
|
||||||
|
|
||||||
|
# 获取总数并设置响应头
|
||||||
|
from fastapi import Response
|
||||||
|
total_query = db.query(Information).filter(Information.status == status)
|
||||||
|
if info_type:
|
||||||
|
total_query = total_query.filter(Information.info_type == info_type)
|
||||||
|
total_count = total_query.count()
|
||||||
|
total_pages = (total_count + page_size - 1) // page_size
|
||||||
|
|
||||||
|
# 设置响应头
|
||||||
|
response.headers['X-Total-Pages'] = str(total_pages)
|
||||||
|
response.headers['X-Total-Count'] = str(total_count)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -182,6 +200,110 @@ def match_collections_count(db: Session, user_id: str, expect_number: str) -> in
|
||||||
return count
|
return count
|
||||||
|
|
||||||
|
|
||||||
|
def match_collections_count_from_coolbot(expect_number: str) -> int:
|
||||||
|
"""根据号码特征计算匹配藏品数量(从coolbot_data数据库)"""
|
||||||
|
if not expect_number or len(expect_number) != 10:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# 固定前缀
|
||||||
|
if not expect_number.startswith('J0'):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
pattern = expect_number[2:] # 后8位
|
||||||
|
if not pattern:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# 直接查询coolbot_data数据库
|
||||||
|
query = text("""
|
||||||
|
SELECT COUNT(*) FROM collections
|
||||||
|
WHERE crown_code IS NOT NULL
|
||||||
|
AND crown_code != ''
|
||||||
|
AND LENGTH(crown_code) >= 10
|
||||||
|
AND crown_code LIKE 'J0%'
|
||||||
|
""")
|
||||||
|
|
||||||
|
try:
|
||||||
|
with coolbot_engine.connect() as conn:
|
||||||
|
result = conn.execute(query)
|
||||||
|
total_count = result.scalar() or 0
|
||||||
|
|
||||||
|
# 遍历匹配
|
||||||
|
query_all = text("""
|
||||||
|
SELECT id, crown_code FROM collections
|
||||||
|
WHERE crown_code IS NOT NULL
|
||||||
|
AND crown_code != ''
|
||||||
|
AND LENGTH(crown_code) >= 10
|
||||||
|
AND crown_code LIKE 'J0%'
|
||||||
|
""")
|
||||||
|
result = conn.execute(query_all)
|
||||||
|
|
||||||
|
match_count = 0
|
||||||
|
for row in result:
|
||||||
|
crown_code = row[1]
|
||||||
|
if crown_code and len(crown_code) >= 10:
|
||||||
|
col_pattern = crown_code[2:10]
|
||||||
|
if match_pattern(col_pattern, pattern):
|
||||||
|
match_count += 1
|
||||||
|
|
||||||
|
return match_count
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error querying coolbot_data: {e}")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def match_collections_list_from_coolbot(expect_number: str, limit: int = 20) -> List[dict]:
|
||||||
|
"""获取匹配的藏品列表(从coolbot_data数据库)"""
|
||||||
|
if not expect_number or len(expect_number) != 10:
|
||||||
|
return []
|
||||||
|
|
||||||
|
# 固定前缀
|
||||||
|
if not expect_number.startswith('J0'):
|
||||||
|
return []
|
||||||
|
|
||||||
|
pattern = expect_number[2:] # 后8位
|
||||||
|
if not pattern:
|
||||||
|
return []
|
||||||
|
|
||||||
|
# 直接查询coolbot_data数据库
|
||||||
|
query = text("""
|
||||||
|
SELECT id, name, category, crown_code, price, post_title, post_url, author, post_crawled_at
|
||||||
|
FROM collections
|
||||||
|
WHERE crown_code IS NOT NULL
|
||||||
|
AND crown_code != ''
|
||||||
|
AND LENGTH(crown_code) >= 10
|
||||||
|
AND crown_code LIKE 'J0%'
|
||||||
|
""")
|
||||||
|
|
||||||
|
try:
|
||||||
|
with coolbot_engine.connect() as conn:
|
||||||
|
result = conn.execute(query)
|
||||||
|
|
||||||
|
matched = []
|
||||||
|
for row in result:
|
||||||
|
crown_code = row[3]
|
||||||
|
if crown_code and len(crown_code) >= 10:
|
||||||
|
col_pattern = crown_code[2:10]
|
||||||
|
if match_pattern(col_pattern, pattern):
|
||||||
|
matched.append({
|
||||||
|
"id": row[0],
|
||||||
|
"name": row[1],
|
||||||
|
"category": row[2],
|
||||||
|
"crown_code": crown_code,
|
||||||
|
"price": float(row[4]) if row[4] else None,
|
||||||
|
"post_title": row[5],
|
||||||
|
"post_url": row[6],
|
||||||
|
"author": row[7],
|
||||||
|
"post_crawled_at": row[8].isoformat() if row[8] else None
|
||||||
|
})
|
||||||
|
if len(matched) >= limit:
|
||||||
|
break
|
||||||
|
|
||||||
|
return matched
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error querying coolbot_data: {e}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
def match_pattern(col_number: str, pattern: str) -> bool:
|
def match_pattern(col_number: str, pattern: str) -> bool:
|
||||||
"""匹配号码特征模式"""
|
"""匹配号码特征模式"""
|
||||||
# X = 任意数字
|
# X = 任意数字
|
||||||
|
|
@ -502,7 +624,36 @@ def get_seek_match(
|
||||||
"cost_price": c.f05_40_cost_price,
|
"cost_price": c.f05_40_cost_price,
|
||||||
}
|
}
|
||||||
for c in matched
|
for c in matched
|
||||||
]
|
],
|
||||||
|
"network_matched_count": match_collections_count_from_coolbot(info.expect_number) if info.expect_number else 0,
|
||||||
|
"network_collections": match_collections_list_from_coolbot(info.expect_number, limit=20) if info.expect_number else []
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# 获取网络数据匹配列表
|
||||||
|
@router.get("/seek/network-match/{info_id}")
|
||||||
|
def get_network_match(
|
||||||
|
info_id: str,
|
||||||
|
limit: int = Query(20, ge=1, le=100),
|
||||||
|
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="寻配号信息不存在")
|
||||||
|
|
||||||
|
if not info.expect_number:
|
||||||
|
return {"matched_count": 0, "collections": []}
|
||||||
|
|
||||||
|
matched = match_collections_list_from_coolbot(info.expect_number, limit=limit)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"matched_count": len(matched),
|
||||||
|
"collections": matched
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -558,8 +709,21 @@ def get_my_seeks(
|
||||||
collection_version=item.collection.f02_11_version 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,
|
collection_number=item.collection.f02_10_prefix_serial if item.collection else None,
|
||||||
matched_count=matched_count,
|
matched_count=matched_count,
|
||||||
|
network_matched_count=match_collections_count_from_coolbot(item.expect_number) if item.info_type == 'seek' and item.expect_number else 0,
|
||||||
))
|
))
|
||||||
|
|
||||||
|
# 获取总数并设置响应头
|
||||||
|
from fastapi import Response
|
||||||
|
total_query = db.query(Information).filter(Information.status == status)
|
||||||
|
if info_type:
|
||||||
|
total_query = total_query.filter(Information.info_type == info_type)
|
||||||
|
total_count = total_query.count()
|
||||||
|
total_pages = (total_count + page_size - 1) // page_size
|
||||||
|
|
||||||
|
# 设置响应头
|
||||||
|
response.headers['X-Total-Pages'] = str(total_pages)
|
||||||
|
response.headers['X-Total-Count'] = str(total_count)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -891,3 +1055,18 @@ def get_publisher_info(
|
||||||
"contact": contact,
|
"contact": contact,
|
||||||
"created_at": info.created_at.isoformat() if info.created_at else None
|
"created_at": info.created_at.isoformat() if info.created_at else None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/yichen-posts")
|
||||||
|
def get_yichen_posts(category: str = None, search: str = None, page: int = 1, page_size: int = 20):
|
||||||
|
from app.models.models import Information
|
||||||
|
from sqlalchemy import desc
|
||||||
|
query = db.query(Information).filter(Information.info_type == 'yichen')
|
||||||
|
if category:
|
||||||
|
query = query.filter(Information.expect_category == category)
|
||||||
|
if search:
|
||||||
|
query = query.filter(Information.title.contains(search))
|
||||||
|
total = query.count()
|
||||||
|
offset = (page - 1) * page_size
|
||||||
|
items = query.order_by(desc(Information.created_at)).offset(offset).limit(page_size).all()
|
||||||
|
return {"data": items, "pagination": {"page": page, "limit": page_size, "total": total}}
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
VERSION=1.2.59
|
VERSION=1.2.50
|
||||||
|
|
|
||||||
|
|
@ -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.53</title>
|
<title>甲辰收藏 v1.2.50</title>
|
||||||
|
|
||||||
<!-- Favicon -->
|
<!-- Favicon -->
|
||||||
<link rel="icon" type="image/png" href="/static/icons/favicon.png" />
|
<link rel="icon" type="image/png" href="/static/icons/favicon.png" />
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ export default function Home() {
|
||||||
const [user, setUser] = useState(null)
|
const [user, setUser] = useState(null)
|
||||||
const [stats, setStats] = useState({ totalCount: 0, totalCost: 0, totalRevenue: 0, expectedProfit: 0, totalProfit: 0, gradedCount: 0 })
|
const [stats, setStats] = useState({ totalCount: 0, totalCost: 0, totalRevenue: 0, expectedProfit: 0, totalProfit: 0, gradedCount: 0 })
|
||||||
const [recentCollections, setRecentCollections] = useState([])
|
const [recentCollections, setRecentCollections] = useState([])
|
||||||
|
const [yichensStats, setYichensStats] = useState({ total: 0, deals: 0, wants: 0, dragons: 0, horses: 0, tianma: 0 })
|
||||||
|
const [recentPosts, setRecentPosts] = useState([])
|
||||||
const currentPath = window.location.hash.slice(1) || '/'
|
const currentPath = window.location.hash.slice(1) || '/'
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -54,6 +56,18 @@ export default function Home() {
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
// 获取一尘看板数据
|
||||||
|
useEffect(() => {
|
||||||
|
fetch('/api/yichens/stats/today').then(res => res.json()).then(data => {
|
||||||
|
setYichensStats(data || {})
|
||||||
|
}).catch(() => {})
|
||||||
|
|
||||||
|
// 获取最新一尘帖子
|
||||||
|
fetch('/api/yichens/posts?limit=10&offset=0').then(res => res.json()).then(data => {
|
||||||
|
setRecentPosts(Array.isArray(data) ? data : [])
|
||||||
|
}).catch(() => {})
|
||||||
|
}, [])
|
||||||
|
|
||||||
// 检查是否为管理员
|
// 检查是否为管理员
|
||||||
const isAdmin = user && user.role === 'admin'
|
const isAdmin = user && user.role === 'admin'
|
||||||
|
|
||||||
|
|
@ -96,7 +110,7 @@ export default function Home() {
|
||||||
backdropFilter: 'blur(10px)'
|
backdropFilter: 'blur(10px)'
|
||||||
}}>
|
}}>
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
|
<div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
|
||||||
<img src="/static/images/jiachenlong-logo.png?v=1.2.4" alt="甲辰收藏" style={{ width: '48px', height: '48px', borderRadius: '50%', objectFit: 'cover' }} />
|
<img src="/static/images/jiachenlong-logo.png" alt="甲辰收藏" style={{ width: '48px', height: '48px', borderRadius: '50%', objectFit: 'cover' }} />
|
||||||
<div>
|
<div>
|
||||||
<div style={{ color: '#fff', fontSize: '18px', fontWeight: '600' }}>{user?.username || '用户'}</div>
|
<div style={{ color: '#fff', fontSize: '18px', fontWeight: '600' }}>{user?.username || '用户'}</div>
|
||||||
<div style={{ color: 'rgba(255,255,255,0.6)', fontSize: '12px' }}>欢迎回来 👋</div>
|
<div style={{ color: 'rgba(255,255,255,0.6)', fontSize: '12px' }}>欢迎回来 👋</div>
|
||||||
|
|
@ -175,11 +189,42 @@ export default function Home() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 最近藏品 */}
|
{/* 一尘今日数据 */}
|
||||||
|
<div style={{ marginBottom: '20px' }}>
|
||||||
|
<div style={{ color: 'rgba(255,255,255,0.7)', fontSize: '14px', marginBottom: '12px', paddingLeft: '4px' }}>📊 一尘今日数据</div>
|
||||||
|
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '8px' }}>
|
||||||
|
<div style={{ background: 'linear-gradient(135deg, rgba(59,130,246,0.15) 0%, rgba(59,130,246,0.05) 100%)', borderRadius: '12px', padding: '12px 8px', border: '1px solid rgba(59,130,246,0.2)', textAlign: 'center' }}>
|
||||||
|
<div style={{ color: '#60a5fa', fontSize: '20px', fontWeight: '700' }}>{yichensStats.total || 0}</div>
|
||||||
|
<div style={{ color: 'rgba(255,255,255,0.5)', fontSize: '10px' }}>总帖子</div>
|
||||||
|
</div>
|
||||||
|
<div style={{ background: 'linear-gradient(135deg, rgba(16,185,129,0.15) 0%, rgba(16,185,129,0.05) 100%)', borderRadius: '12px', padding: '12px 8px', border: '1px solid rgba(16,185,129,0.2)', textAlign: 'center' }}>
|
||||||
|
<div style={{ color: '#34d399', fontSize: '20px', fontWeight: '700' }}>{yichensStats.deals || 0}</div>
|
||||||
|
<div style={{ color: 'rgba(255,255,255,0.5)', fontSize: '10px' }}>出售</div>
|
||||||
|
</div>
|
||||||
|
<div style={{ background: 'linear-gradient(135deg, rgba(245,158,11,0.15) 0%, rgba(245,158,11,0.05) 100%)', borderRadius: '12px', padding: '12px 8px', border: '1px solid rgba(245,158,11,0.2)', textAlign: 'center' }}>
|
||||||
|
<div style={{ color: '#fbbf24', fontSize: '20px', fontWeight: '700' }}>{yichensStats.wants || 0}</div>
|
||||||
|
<div style={{ color: 'rgba(255,255,255,0.5)', fontSize: '10px' }}>求购</div>
|
||||||
|
</div>
|
||||||
|
<div style={{ background: 'linear-gradient(135deg, rgba(139,92,246,0.15) 0%, rgba(139,92,246,0.05) 100%)', borderRadius: '12px', padding: '12px 8px', border: '1px solid rgba(139,92,246,0.2)', textAlign: 'center' }}>
|
||||||
|
<div style={{ color: '#a78bfa', fontSize: '20px', fontWeight: '700' }}>{yichensStats.dragons || 0}</div>
|
||||||
|
<div style={{ color: 'rgba(255,255,255,0.5)', fontSize: '10px' }}>龙钞</div>
|
||||||
|
</div>
|
||||||
|
<div style={{ background: 'linear-gradient(135deg, rgba(236,72,153,0.15) 0%, rgba(236,72,153,0.05) 100%)', borderRadius: '12px', padding: '12px 8px', border: '1px solid rgba(236,72,153,0.2)', textAlign: 'center' }}>
|
||||||
|
<div style={{ color: '#f472b6', fontSize: '20px', fontWeight: '700' }}>{yichensStats.horses || 0}</div>
|
||||||
|
<div style={{ color: 'rgba(255,255,255,0.5)', fontSize: '10px' }}>马钞</div>
|
||||||
|
</div>
|
||||||
|
<div style={{ background: 'linear-gradient(135deg, rgba(20,184,166,0.15) 0%, rgba(20,184,166,0.05) 100%)', borderRadius: '12px', padding: '12px 8px', border: '1px solid rgba(20,184,166,0.2)', textAlign: 'center' }}>
|
||||||
|
<div style={{ color: '#2dd4bf', fontSize: '20px', fontWeight: '700' }}>{yichensStats.tianma || 0}</div>
|
||||||
|
<div style={{ color: 'rgba(255,255,255,0.5)', fontSize: '10px' }}>天马</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 最新一尘发帖 */}
|
||||||
<div>
|
<div>
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '12px', paddingLeft: '4px' }}>
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '12px', paddingLeft: '4px' }}>
|
||||||
<div style={{ color: 'rgba(255,255,255,0.7)', fontSize: '14px' }}>最近藏品</div>
|
<div style={{ color: 'rgba(255,255,255,0.7)', fontSize: '14px' }}>📝 最新一尘发帖</div>
|
||||||
<div onClick={() => window.location.hash = '#/list'} style={{ color: '#3b82f6', fontSize: '12px', cursor: 'pointer' }}>查看全部 →</div>
|
<div onClick={() => window.location.hash = '#/news'} style={{ color: '#3b82f6', fontSize: '12px', cursor: 'pointer' }}>查看全部 →</div>
|
||||||
</div>
|
</div>
|
||||||
<div style={{
|
<div style={{
|
||||||
background: 'rgba(255,255,255,0.03)',
|
background: 'rgba(255,255,255,0.03)',
|
||||||
|
|
@ -187,24 +232,30 @@ export default function Home() {
|
||||||
border: '1px solid rgba(255,255,255,0.05)',
|
border: '1px solid rgba(255,255,255,0.05)',
|
||||||
overflow: 'hidden'
|
overflow: 'hidden'
|
||||||
}}>
|
}}>
|
||||||
{recentCollections.length === 0 ? (
|
{recentPosts.length === 0 ? (
|
||||||
<div style={{ padding: '40px', textAlign: 'center', color: 'rgba(255,255,255,0.3)' }}>暂无藏品</div>
|
<div style={{ padding: '40px', textAlign: 'center', color: 'rgba(255,255,255,0.3)' }}>暂无帖子</div>
|
||||||
) : (
|
) : (
|
||||||
recentCollections.map((item, idx) => (
|
recentPosts.map((item, idx) => (
|
||||||
<div key={item.id || idx} onClick={() => window.location.hash = '#/detail?id=' + item.id} style={{
|
<div key={item.post_id || idx} onClick={() => window.location.hash = '#/news'} style={{
|
||||||
padding: '12px 16px',
|
padding: '12px 16px',
|
||||||
borderBottom: idx < recentCollections.length - 1 ? '1px solid rgba(255,255,255,0.05)' : 'none',
|
borderBottom: idx < recentPosts.length - 1 ? '1px solid rgba(255,255,255,0.05)' : 'none',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
alignItems: 'center'
|
alignItems: 'center'
|
||||||
}}>
|
}}>
|
||||||
<div>
|
<div style={{ flex: 1 }}>
|
||||||
<div style={{ color: '#fff', fontSize: '14px' }}>{item.code || '-'} <span style={{ color: 'rgba(255,255,255,0.4)', fontSize: '14px', fontWeight: 'bold' }}>{item.prefixSerial || ''}</span></div>
|
<div style={{ color: '#fff', fontSize: '14px' }}>{item.title || '无标题'}</div>
|
||||||
<div style={{ color: 'rgba(255,255,255,0.4)', fontSize: '12px', marginTop: '2px' }}>{item.version || '-'} · {item.status === 'sold' ? '已售' : item.status === 'in_collection' ? '收藏中' : item.status}</div>
|
<div style={{ color: 'rgba(255,255,255,0.4)', fontSize: '12px', marginTop: '2px' }}>{item.category || '-'} · {item.author_username || '未知'} · {item.post_time?.substring(0, 16) || ''}</div>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ color: item.costPrice ? '#22c55e' : 'rgba(255,255,255,0.3)', fontSize: '13px' }}>
|
<div style={{
|
||||||
{item.costPrice ? '¥' + item.costPrice : '-'}
|
color: item.post_type === 'deal' ? '#10b981' : item.post_type === 'want' ? '#f59e0b' : '#8b5cf6',
|
||||||
|
fontSize: '12px',
|
||||||
|
padding: '2px 8px',
|
||||||
|
borderRadius: '4px',
|
||||||
|
background: item.post_type === 'deal' ? 'rgba(16,185,129,0.2)' : item.post_type === 'want' ? 'rgba(245,158,11,0.2)' : 'rgba(139,92,246,0.2)'
|
||||||
|
}}>
|
||||||
|
{item.post_type === 'deal' ? '出售' : item.post_type === 'want' ? '求购' : '其他'}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
|
|
|
||||||
|
|
@ -675,3 +675,6 @@ export default function Info() {
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import React, { useState, useEffect } from 'react'
|
import React, { useState, useEffect } from 'react'
|
||||||
|
import YichensBoard from './YichensBoard'
|
||||||
|
|
||||||
// 本地获取用户手机号 - 添加异常处理
|
// 本地获取用户手机号 - 添加异常处理
|
||||||
const getUserPhone = () => {
|
const getUserPhone = () => {
|
||||||
|
|
@ -22,7 +23,7 @@ const getStoredUserId = () => {
|
||||||
|
|
||||||
// 资讯页面 - 展示寻配号和行情信息(所有人可见)
|
// 资讯页面 - 展示寻配号和行情信息(所有人可见)
|
||||||
export default function News() {
|
export default function News() {
|
||||||
const [activeTab, setActiveTab] = useState(localStorage.getItem('news_activeTab') || 'seek')
|
const [activeTab, setActiveTab] = useState(localStorage.getItem('news_activeTab') || 'yichen')
|
||||||
const [showSeekPublish, setShowSeekPublish] = useState(false)
|
const [showSeekPublish, setShowSeekPublish] = useState(false)
|
||||||
const [showMySeeks, setShowMySeeks] = useState(false) // 我的寻号
|
const [showMySeeks, setShowMySeeks] = useState(false) // 我的寻号
|
||||||
const [showContactId, setShowContactId] = useState(null) // 显示联系方式的寻号ID
|
const [showContactId, setShowContactId] = useState(null) // 显示联系方式的寻号ID
|
||||||
|
|
@ -32,6 +33,8 @@ export default function News() {
|
||||||
const [matchedStatus, setMatchedStatus] = useState({}) // 存储各寻号的匹配状态
|
const [matchedStatus, setMatchedStatus] = useState({}) // 存储各寻号的匹配状态
|
||||||
const [showMatchList, setShowMatchList] = useState(false)
|
const [showMatchList, setShowMatchList] = useState(false)
|
||||||
const [matchCollections, setMatchCollections] = useState([])
|
const [matchCollections, setMatchCollections] = useState([])
|
||||||
|
const [networkMatchCollections, setNetworkMatchCollections] = useState([])
|
||||||
|
const [showNetworkMatchList, setShowNetworkMatchList] = useState(false)
|
||||||
const [customModal, setCustomModal] = useState({show: false, title: '', content: ''})
|
const [customModal, setCustomModal] = useState({show: false, title: '', content: ''})
|
||||||
const [seekForm, setSeekForm] = useState({
|
const [seekForm, setSeekForm] = useState({
|
||||||
edition: '龙钞', type: '', category: '寻号', price: '', matchType: '模糊', features: '', content: '', title: '', contact: getUserPhone() || ''
|
edition: '龙钞', type: '', category: '寻号', price: '', matchType: '模糊', features: '', content: '', title: '', contact: getUserPhone() || ''
|
||||||
|
|
@ -40,6 +43,8 @@ export default function News() {
|
||||||
const [viewMode, setViewMode] = useState('all')
|
const [viewMode, setViewMode] = useState('all')
|
||||||
const [expandedItems, setExpandedItems] = useState({}) // 展开状态
|
const [expandedItems, setExpandedItems] = useState({}) // 展开状态
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
|
const [currentPage, setCurrentPage] = useState(1)
|
||||||
|
const [totalPages, setTotalPages] = useState(1)
|
||||||
|
|
||||||
const API_BASE = localStorage.getItem('API_BASE') || ''
|
const API_BASE = localStorage.getItem('API_BASE') || ''
|
||||||
const currentUser = localStorage.getItem('token') ? { id: 'logged_in' } : null
|
const currentUser = localStorage.getItem('token') ? { id: 'logged_in' } : null
|
||||||
|
|
@ -69,10 +74,13 @@ export default function News() {
|
||||||
// 寻配号对所有人公开,无需登录即可查看
|
// 寻配号对所有人公开,无需登录即可查看
|
||||||
const token = localStorage.getItem('token')
|
const token = localStorage.getItem('token')
|
||||||
// 根据tab获取不同类型的数据
|
// 根据tab获取不同类型的数据
|
||||||
const type = activeTab === 'seek' ? 'seek' : 'deal'
|
const type = activeTab === 'seek' ? 'seek' : activeTab === 'deal' ? 'deal' : 'yichen'
|
||||||
// 始终传递token,以便获取准确的matched_count
|
// 始终传递token,以便获取准确的matched_count
|
||||||
const headers = token ? { Authorization: `Bearer ${token}` } : {}
|
const headers = token ? { Authorization: `Bearer ${token}` } : {}
|
||||||
const res = await fetch(`${API_BASE}/api/information/list?info_type=${type}`, { headers })
|
const res = await fetch(`${API_BASE}/api/information/list?info_type=${type}&page=${currentPage}&page_size=50`, { headers })
|
||||||
|
// 从响应头获取总页数
|
||||||
|
const total = res.headers.get('X-Total-Pages') || res.headers.get('x-total-pages')
|
||||||
|
if (total) setTotalPages(parseInt(total))
|
||||||
// 检查响应状态
|
// 检查响应状态
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
console.error('获取资讯列表失败:', res.status)
|
console.error('获取资讯列表失败:', res.status)
|
||||||
|
|
@ -146,10 +154,11 @@ export default function News() {
|
||||||
const token = localStorage.getItem('token')
|
const token = localStorage.getItem('token')
|
||||||
if (!token) { alert('请先登录'); return }
|
if (!token) { alert('请先登录'); return }
|
||||||
try {
|
try {
|
||||||
|
const phone = getUserPhone()
|
||||||
const res = await fetch(`${API_BASE}/api/information/seek/match-confirm`, {
|
const res = await fetch(`${API_BASE}/api/information/seek/match-confirm`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },
|
headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ info_id: infoId })
|
body: JSON.stringify({ info_id: infoId, contact: phone || '用户已同意' })
|
||||||
})
|
})
|
||||||
const data = await res.json()
|
const data = await res.json()
|
||||||
if (data.message === '匹配成功,已通知发布者') {
|
if (data.message === '匹配成功,已通知发布者') {
|
||||||
|
|
@ -187,7 +196,7 @@ export default function News() {
|
||||||
console.log('Matched user response:', res.status)
|
console.log('Matched user response:', res.status)
|
||||||
const data = await res.json()
|
const data = await res.json()
|
||||||
console.log('Matched user data:', data)
|
console.log('Matched user data:', data)
|
||||||
if (data.user_name) setCustomModal({show: true, title: '匹配者信息', content: `用户名: ${data.user_name}\n联系方式: ${data.matched_contact || '未提供'}`})
|
if (data.user_name) setCustomModal({show: true, title: '匹配者信息', content: `用户名: ${data.user_name}\n手机号: ${data.phone || '未提供'}\n\n免责声明及风险提示:所有用户信息仅作参考,交易请走正规平台,如产生经济损失与本站无关,后果自负。`})
|
||||||
else if (data.detail) alert(data.detail)
|
else if (data.detail) alert(data.detail)
|
||||||
} catch (e) { console.error(e) }
|
} catch (e) { console.error(e) }
|
||||||
}
|
}
|
||||||
|
|
@ -200,7 +209,7 @@ export default function News() {
|
||||||
console.log('Publisher response:', res.status)
|
console.log('Publisher response:', res.status)
|
||||||
const data = await res.json()
|
const data = await res.json()
|
||||||
console.log('Publisher data:', data)
|
console.log('Publisher data:', data)
|
||||||
if (data.user_name) setCustomModal({show: true, title: '发布者信息', content: `用户名: ${data.user_name}\n联系方式: ${data.contact || '未提供'}`})
|
if (data.user_name) setCustomModal({show: true, title: '发布者信息', content: `用户名: ${data.user_name}\n手机号: ${data.phone || '未提供'}\n\n免责声明及风险提示:所有用户信息仅作参考,交易请走正规平台,如产生经济损失与本站无关,后果自负。`})
|
||||||
else if (data.detail) alert(data.detail)
|
else if (data.detail) alert(data.detail)
|
||||||
} catch (e) { console.error(e) }
|
} catch (e) { console.error(e) }
|
||||||
}
|
}
|
||||||
|
|
@ -223,6 +232,20 @@ export default function News() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取网络数据匹配列表
|
||||||
|
const fetchNetworkMatchCollections = async (infoId) => {
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${API_BASE}/api/information/seek/network-match/${infoId}`)
|
||||||
|
const data = await res.json()
|
||||||
|
console.log('网络数据匹配结果:', data)
|
||||||
|
setNetworkMatchCollections(data.collections || [])
|
||||||
|
setShowNetworkMatchList(true)
|
||||||
|
} catch (e) {
|
||||||
|
console.error('获取网络匹配藏品失败:', e)
|
||||||
|
alert('获取网络匹配藏品失败: ' + e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 获取当前用户ID
|
// 获取当前用户ID
|
||||||
const currentUserId = getStoredUserId()
|
const currentUserId = getStoredUserId()
|
||||||
|
|
||||||
|
|
@ -315,7 +338,8 @@ export default function News() {
|
||||||
// Tab切换
|
// Tab切换
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{ key: 'seek', label: '🔍 寻配号' },
|
{ key: 'seek', label: '🔍 寻配号' },
|
||||||
{ key: 'deal', label: '💰 成交行情' }
|
{ key: 'deal', label: '💰 成交行情' },
|
||||||
|
{ key: 'yichen', label: '📊 一尘看板' }
|
||||||
]
|
]
|
||||||
|
|
||||||
const formatDate = (dateStr) => {
|
const formatDate = (dateStr) => {
|
||||||
|
|
@ -387,7 +411,7 @@ export default function News() {
|
||||||
readOnly={isFixed}
|
readOnly={isFixed}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
if (i < 2) return
|
if (i < 2) return
|
||||||
const val = e.target.value.toUpperCase().replace(/[^0-9XABCFG]/g, '')
|
const val = e.target.value.toUpperCase().replace(/[^0-9XABCDEFG]/g, '')
|
||||||
const newFeatures = (seekForm.features || '').split('')
|
const newFeatures = (seekForm.features || '').split('')
|
||||||
while (newFeatures.length < 8) newFeatures.push('')
|
while (newFeatures.length < 8) newFeatures.push('')
|
||||||
newFeatures[i - 2] = val
|
newFeatures[i - 2] = val
|
||||||
|
|
@ -435,7 +459,7 @@ export default function News() {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<h3 style={{ color: '#fff', marginBottom: '16px' }}>
|
<h3 style={{ color: '#fff', marginBottom: '16px' }}>
|
||||||
{activeTab === 'seek' ? '🔍 寻配号信息' : '💰 成交行情信息'}
|
{activeTab === 'seek' ? '🔍 寻配号信息' : activeTab === 'deal' ? '💰 成交行情信息' : '📊 一尘看板'}
|
||||||
{activeTab === 'seek' && (
|
{activeTab === 'seek' && (
|
||||||
<div style={{ display: 'flex', gap: '8px', marginLeft: 'auto' }}>
|
<div style={{ display: 'flex', gap: '8px', marginLeft: 'auto' }}>
|
||||||
<button onClick={() => { setViewMode('all'); fetchInfoList(); }} style={{ padding: '6px 12px', background: viewMode==='all'?'#3b82f6':'#1e293b', border: '1px solid #334155', borderRadius: '6px', color: '#fff', fontSize: '12px' }}>全部</button>
|
<button onClick={() => { setViewMode('all'); fetchInfoList(); }} style={{ padding: '6px 12px', background: viewMode==='all'?'#3b82f6':'#1e293b', border: '1px solid #334155', borderRadius: '6px', color: '#fff', fontSize: '12px' }}>全部</button>
|
||||||
|
|
@ -450,11 +474,11 @@ export default function News() {
|
||||||
<div style={{ color: '#94a3b8', textAlign: 'center', padding: '40px' }}>加载中...</div>
|
<div style={{ color: '#94a3b8', textAlign: 'center', padding: '40px' }}>加载中...</div>
|
||||||
) : infoList.length === 0 ? (
|
) : infoList.length === 0 ? (
|
||||||
<div style={{ color: '#94a3b8', textAlign: 'center', padding: '40px' }}>
|
<div style={{ color: '#94a3b8', textAlign: 'center', padding: '40px' }}>
|
||||||
暂无{activeTab === 'seek' ? '寻配号' : '成交行情'}信息
|
暂无{activeTab === 'seek' ? '寻配号' : activeTab === 'deal' ? '成交行情' : '一尘看板'}信息
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
|
||||||
{infoList.map(item => {
|
{activeTab === 'yichen' ? <YichensBoard /> : infoList.map(item => {
|
||||||
// 解析正文中的号码特征和联系方式
|
// 解析正文中的号码特征和联系方式
|
||||||
const content = item.content || ''
|
const content = item.content || ''
|
||||||
const featuresMatch = content.match(/号码特征[:\s]*(.+?)(?:\n|$)/)
|
const featuresMatch = content.match(/号码特征[:\s]*(.+?)(?:\n|$)/)
|
||||||
|
|
@ -472,7 +496,7 @@ export default function News() {
|
||||||
</div>
|
</div>
|
||||||
{/* 创建日期 + 用户名 */}
|
{/* 创建日期 + 用户名 */}
|
||||||
<div style={{ color: '#64748b', fontSize: '12px', marginBottom: '8px' }}>
|
<div style={{ color: '#64748b', fontSize: '12px', marginBottom: '8px' }}>
|
||||||
📅 {formatDate(item.created_at)} | 👤 {item.user_name || '匿名用户'}
|
📅 {formatDate(item.created_at)} | 👤 {(item.content && item.content.match(/发布者: (.+)/)?.[1]) || item.user_name || '匿名用户'}
|
||||||
</div>
|
</div>
|
||||||
{/* 号码特征 */}
|
{/* 号码特征 */}
|
||||||
{features && (
|
{features && (
|
||||||
|
|
@ -525,8 +549,16 @@ export default function News() {
|
||||||
style={{ color: '#10b981', fontSize: '13px', fontWeight: 'bold', cursor: 'pointer' }}
|
style={{ color: '#10b981', fontSize: '13px', fontWeight: 'bold', cursor: 'pointer' }}
|
||||||
onClick={() => fetchMatchCollections(item.id)}
|
onClick={() => fetchMatchCollections(item.id)}
|
||||||
>
|
>
|
||||||
配号结果: {item.matched_count || 0}条藏品匹配成功
|
自有{item.matched_count || 0}条藏品匹配成功
|
||||||
</span>
|
</span>
|
||||||
|
{item.network_matched_count !== undefined && (
|
||||||
|
<span
|
||||||
|
style={{ color: '#3b82f6', fontSize: '13px', fontWeight: 'bold', cursor: 'pointer', marginLeft: '12px' }}
|
||||||
|
onClick={() => fetchNetworkMatchCollections(item.id)}
|
||||||
|
>
|
||||||
|
网络数据{item.network_matched_count}条匹配成功
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{/* 正文 - 默认收起,点击展开 */}
|
{/* 正文 - 默认收起,点击展开 */}
|
||||||
|
|
@ -613,23 +645,24 @@ export default function News() {
|
||||||
<div style={{ display: 'flex', gap: '8px', marginTop: '8px' }}>
|
<div style={{ display: 'flex', gap: '8px', marginTop: '8px' }}>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
if (item.is_matched === 'matched') return
|
||||||
if (item.matched_count > 0) {
|
if (item.matched_count > 0) {
|
||||||
matchAndContact(item.id)
|
matchAndContact(item.id)
|
||||||
} else {
|
} else {
|
||||||
setCustomModal({show: true, title: '提示', content: '暂无匹配藏品,无法匹配'})
|
setCustomModal({show: true, title: '提示', content: '暂无匹配藏品,无法匹配'})
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
disabled={item.matched_count === 0}
|
disabled={item.matched_count === 0 || item.is_matched === 'matched'}
|
||||||
style={{
|
style={{
|
||||||
flex: 1,
|
flex: 1,
|
||||||
padding: '8px 12px',
|
padding: '8px 12px',
|
||||||
borderRadius: '6px',
|
borderRadius: '6px',
|
||||||
border: 'none',
|
border: 'none',
|
||||||
fontSize: '12px',
|
fontSize: '12px',
|
||||||
background: item.matched_count > 0 ? '#3b82f6' : '#4b5563',
|
background: (item.matched_count > 0 && item.is_matched !== 'matched') ? '#3b82f6' : '#4b5563',
|
||||||
color: '#fff',
|
color: '#fff',
|
||||||
cursor: item.matched_count > 0 ? 'pointer' : 'not-allowed',
|
cursor: (item.matched_count > 0 && item.is_matched !== 'matched') ? 'pointer' : 'not-allowed',
|
||||||
opacity: item.matched_count > 0 ? 1 : 0.5
|
opacity: (item.matched_count > 0 && item.is_matched !== 'matched') ? 1 : 0.5
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
匹配并联系藏友
|
匹配并联系藏友
|
||||||
|
|
@ -751,6 +784,74 @@ export default function News() {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* 网络数据匹配藏品列表弹窗 */}
|
||||||
|
{showNetworkMatchList && (
|
||||||
|
<div style={{ position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, background: 'rgba(0,0,0,0.8)', zIndex: 1000, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||||
|
<div style={{ background: '#1e293b', borderRadius: '12px', padding: '20px', width: '90%', maxHeight: '80vh', overflow: 'auto' }}>
|
||||||
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '16px' }}>
|
||||||
|
<h3 style={{ color: '#fff', margin: 0 }}>匹配藏品清单(网络数据)</h3>
|
||||||
|
<button onClick={() => setShowNetworkMatchList(false)} style={{ background: 'transparent', border: 'none', color: '#94a3b8', fontSize: '20px', cursor: 'pointer' }}>×</button>
|
||||||
|
</div>
|
||||||
|
{networkMatchCollections.length === 0 ? (
|
||||||
|
<div style={{ color: '#94a3b8', textAlign: 'center', padding: '20px' }}>暂无匹配藏品</div>
|
||||||
|
) : (
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
||||||
|
{networkMatchCollections.map(c => (
|
||||||
|
<div
|
||||||
|
key={c.id}
|
||||||
|
style={{ background: '#0f172a', borderRadius: '8px', padding: '12px', cursor: 'pointer' }}
|
||||||
|
onClick={() => {
|
||||||
|
if (c.post_url) {
|
||||||
|
window.open(c.post_url, '_blank')
|
||||||
|
}
|
||||||
|
setShowNetworkMatchList(false)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ color: '#e2e8f0', fontSize: '13px' }}>
|
||||||
|
<span style={{ color: '#94a3b8' }}>名称: </span>{c.name || '-'}
|
||||||
|
</div>
|
||||||
|
<div style={{ color: '#e2e8f0', fontSize: '13px', marginTop: '4px' }}>
|
||||||
|
<span style={{ color: '#94a3b8' }}>冠字号: </span>{c.crown_code}
|
||||||
|
</div>
|
||||||
|
{c.price && (
|
||||||
|
<div style={{ color: '#e2e8f0', fontSize: '13px', marginTop: '4px' }}>
|
||||||
|
<span style={{ color: '#94a3b8' }}>价格: </span>¥{c.price}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div style={{ color: '#3b82f6', fontSize: '12px', marginTop: '6px' }}>
|
||||||
|
点击查看原帖 →
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 分页组件 */}
|
||||||
|
{totalPages > 1 && (
|
||||||
|
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', gap: '8px', padding: '16px', marginTop: '8px' }}>
|
||||||
|
<button
|
||||||
|
onClick={() => { if (currentPage > 1) { setCurrentPage(currentPage - 1); fetchInfoList() } }}
|
||||||
|
disabled={currentPage === 1}
|
||||||
|
style={{ padding: '8px 16px', borderRadius: '6px', border: 'none', background: currentPage === 1 ? '#374151' : '#3b82f6', color: currentPage === 1 ? '#6b7280' : '#fff', cursor: currentPage === 1 ? 'not-allowed' : 'pointer', fontSize: '13px' }}
|
||||||
|
>
|
||||||
|
上一页
|
||||||
|
</button>
|
||||||
|
<span style={{ color: '#94a3b8', fontSize: '13px' }}>
|
||||||
|
第 {currentPage} / {totalPages} 页
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
onClick={() => { if (currentPage < totalPages) { setCurrentPage(currentPage + 1); fetchInfoList() } }}
|
||||||
|
disabled={currentPage >= totalPages}
|
||||||
|
style={{ padding: '8px 16px', borderRadius: '6px', border: 'none', background: currentPage >= totalPages ? '#374151' : '#3b82f6', color: currentPage >= totalPages ? '#6b7280' : '#fff', cursor: currentPage >= totalPages ? 'not-allowed' : 'pointer', fontSize: '13px' }}
|
||||||
|
>
|
||||||
|
下一页
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* 我的寻号弹窗 */}
|
{/* 我的寻号弹窗 */}
|
||||||
{showMySeeks && (
|
{showMySeeks && (
|
||||||
<div style={{ position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, background: 'rgba(0,0,0,0.8)', zIndex: 1000, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
<div style={{ position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, background: 'rgba(0,0,0,0.8)', zIndex: 1000, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue