v0.1.0 - 认购功能完善版

This commit is contained in:
甲辰生产 2026-04-29 20:48:41 +08:00
parent ee728a5f4e
commit 1fef5177f3
6 changed files with 484 additions and 67 deletions

View File

@ -1,8 +1,8 @@
# purchase - 认购群模型
# Version: 0.0.1 (2026-04-24)
# Version: 0.0.2 (2026-04-29)
# 认购群、成员、藏品管理
from sqlalchemy import Column, String, Text, Integer, DECIMAL, DateTime, ForeignKey
from sqlalchemy import Column, String, Text, Integer, DECIMAL, DateTime, ForeignKey, Boolean
from sqlalchemy.sql import func
from app.core.database import Base
import uuid
@ -15,14 +15,19 @@ class PurchaseGroup(Base):
__tablename__ = "purchase_group"
id = Column(String(36), primary_key=True, default=generate_uuid)
group_code = Column(String(20), nullable=False, unique=True) # 年+序号,如 26001
name = Column(String(100), nullable=False) # 认购群名称
description = Column(Text, nullable=True) # 描述
creator_id = Column(String(36), nullable=False) # 创建者ID
creator_name = Column(String(50), nullable=True) # 创建者名字
status = Column(String(20), default="active") # active/closed
total_members = Column(Integer, default=0) # 总参与人数
total_pending = Column(Integer, default=0) # 待审批人数
total_collections = Column(Integer, default=0) # 总藏品数
total_confirmed = Column(Integer, default=0) # 已确认数
total_amount = Column(DECIMAL(12,2), default=0) # 总金额
cycle_months = Column(Integer, nullable=True) # 认购周期(月)
start_date = Column(DateTime(timezone=True), nullable=True) # 开始日期
avg_price = Column(DECIMAL(10,2), default=0) # 均价
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
@ -37,6 +42,13 @@ class PurchaseMember(Base):
user_name = Column(String(50), nullable=True)
user_avatar = Column(String(255), nullable=True)
role = Column(String(20), default="member") # creator/admin/member
status = Column(String(20), default="pending") # pending/approved/rejected
# 成员扩展信息
wechat_name = Column(String(50), nullable=True) # 微信名
contact = Column(String(50), nullable=True) # 联系方式
purchase_count = Column(Integer, default=0) # 认购数量
paid_amount = Column(DECIMAL(12,2), default=0) # 已付认购款
notes = Column(Text, nullable=True) # 备注
joined_at = Column(DateTime(timezone=True), server_default=func.now())
class PurchaseCollection(Base):
@ -48,8 +60,13 @@ class PurchaseCollection(Base):
user_id = Column(String(36), nullable=False)
user_name = Column(String(50), nullable=True)
collection_name = Column(String(100), nullable=True) # 藏品名称
collection_code = Column(String(50), nullable=True) # 藏品编号
number = Column(String(50), nullable=True) # 冠字号
price = Column(DECIMAL(10,2), nullable=False) # 认购价格
collection_code = Column(String(50), nullable=True) # 藏品编号 (自动生成001开始)
number = Column(String(50), nullable=False) # 冠字号 (必填)
grading = Column(String(50), nullable=False) # 评级 (必填)
boss_name = Column(String(50), nullable=False) # 认购老板 (必填,从成员中选择)
price = Column(DECIMAL(10,2), nullable=False) # 认购价格 (必填)
source = Column(String(20), nullable=False) # 来源: yichen/live/friend/other (必填)
paid = Column(Boolean, default=False) # 认购款是否结清
submit_date = Column(DateTime(timezone=True), server_default=func.now()) # 提交日期
status = Column(String(20), default="pending") # pending/confirmed
submitted_at = Column(DateTime(timezone=True), server_default=func.now())

View File

@ -1,5 +1,5 @@
# purchase - 认购群API路由
# Version: 0.0.1 (2026-04-24)
# Version: 0.0.5 (2026-04-29)
# 认购群、成员、藏品管理
from fastapi import APIRouter, Depends, HTTPException, Query
@ -16,27 +16,92 @@ router = APIRouter(prefix="/api/purchase", tags=["认购"])
# ============ Schema ============
class PurchaseGroupCreate(BaseModel):
cycle_months: Optional[int] = None
start_date: Optional[str] = None
name: str
description: Optional[str] = None
class PurchaseGroupResponse(BaseModel):
cycle_months: Optional[int] = None
start_date: Optional[datetime] = None
id: str
group_code: str
name: str
description: Optional[str]
creator_id: str
creator_name: Optional[str]
status: str
total_members: int
total_pending: int
total_collections: int
total_confirmed: int
total_amount: float
avg_price: float
created_at: Optional[datetime]
class PurchaseCollectionCreate(BaseModel):
collection_name: str
collection_code: Optional[str] = None
number: Optional[str] = None
number: str
grading: str
boss_name: str
price: float
source: str
paid: bool = False
class PurchaseCollectionUpdate(BaseModel):
collection_name: Optional[str] = None
number: Optional[str] = None
grading: Optional[str] = None
boss_name: Optional[str] = None
price: Optional[float] = None
source: Optional[str] = None
paid: Optional[bool] = None
status: Optional[str] = None
class PurchaseCollectionResponse(BaseModel):
id: str
group_id: str
user_id: str
user_name: Optional[str]
collection_name: Optional[str]
collection_code: Optional[str]
number: str
grading: str
boss_name: str
price: float
source: str
paid: bool
status: str
submit_date: Optional[datetime]
submitted_at: Optional[datetime]
class MemberResponse(BaseModel):
id: str
user_id: str
user_name: Optional[str]
user_avatar: Optional[str]
role: str
status: str
joined_at: Optional[datetime]
# ============ 工具函数 ============
def generate_group_code(db: Session) -> str:
"""生成群编码:年+序号,如 26001"""
year = datetime.now().year % 100 # 26
# 查找当前年份最大的编号
last = db.query(PurchaseGroup).filter(
PurchaseGroup.group_code.like(f"{year}%")
).order_by(PurchaseGroup.group_code.desc()).first()
if last and last.group_code:
try:
num = int(last.group_code) + 1
except:
num = 1
else:
num = 1
return f"{year}{num:03d}" # 26001
# ============ 认购群API ============
@ -59,7 +124,8 @@ def get_my_groups(
):
"""获取我参与的认购群"""
member_groups = db.query(PurchaseMember).filter(
PurchaseMember.user_id == current_user.f99_90_id
PurchaseMember.user_id == current_user.f99_90_id,
PurchaseMember.status == "approved"
).all()
group_ids = [m.group_id for m in member_groups]
@ -69,45 +135,123 @@ def get_my_groups(
return groups
@router.get("/groups/{group_id}/pending-members", response_model=List[MemberResponse])
def get_pending_members(
group_id: str,
current_user = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""获取待审批成员列表"""
group = db.query(PurchaseGroup).filter(PurchaseGroup.id == group_id).first()
if not group:
raise HTTPException(status_code=404, detail="认购群不存在")
if group.creator_id != current_user.f99_90_id:
raise HTTPException(status_code=403, detail="只有群主可以查看")
members = db.query(PurchaseMember).filter(
PurchaseMember.group_id == group_id,
PurchaseMember.status == "pending"
).order_by(PurchaseMember.joined_at.asc()).all()
return members
@router.post("/groups/{group_id}/members/{member_id}/approve")
def approve_member(
group_id: str,
member_id: str,
action: str = Query(..., description="approve/reject"),
current_user = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""审批或拒绝成员"""
group = db.query(PurchaseGroup).filter(PurchaseGroup.id == group_id).first()
if not group:
raise HTTPException(status_code=404, detail="认购群不存在")
if group.creator_id != current_user.f99_90_id:
raise HTTPException(status_code=403, detail="只有群主可以审批")
member = db.query(PurchaseMember).filter(
PurchaseMember.id == member_id,
PurchaseMember.group_id == group_id
).first()
if not member:
raise HTTPException(status_code=404, detail="成员不存在")
if action == "approve":
member.status = "approved"
group.total_members += 1
group.total_pending -= 1
db.commit()
return {"message": "已批准加入"}
elif action == "reject":
member.status = "rejected"
group.total_pending -= 1
db.commit()
return {"message": "已拒绝加入"}
else:
raise HTTPException(status_code=400, detail="无效操作")
@router.post("/groups", response_model=PurchaseGroupResponse)
def create_group(
data: PurchaseGroupCreate,
current_user = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""创建认购群(创建者即为群主)"""
"""创建认购群"""
if not current_user:
raise HTTPException(status_code=401, detail="请先登录")
# 普通用户最多创建3个认购群
if current_user.role not in ["admin", "superadmin"]:
existing_count = db.query(PurchaseGroup).filter(
PurchaseGroup.creator_id == current_user.f99_90_id
).count()
if existing_count >= 3:
raise HTTPException(status_code=400, detail="普通用户最多创建3个认购群")
group_code = generate_group_code(db)
# 处理日期
start_date = None
if data.start_date:
try:
start_date = datetime.fromisoformat(data.start_date.replace('Z', '+08:00'))
except:
start_date = datetime.now()
group = PurchaseGroup(
group_code=group_code,
name=data.name,
description=data.description,
creator_id=current_user.f99_90_id,
creator_name=current_user.f01_01_name,
status="active"
status="active",
cycle_months=data.cycle_months,
start_date=start_date
)
db.add(group)
db.commit()
# 创建者自动加入并成为群主
member = PurchaseMember(
group_id=group.id,
user_id=current_user.f99_90_id,
user_name=current_user.f01_01_name,
user_avatar=current_user.avatar,
role="creator"
role="creator",
status="approved"
)
db.add(member)
group.total_members = 1
db.commit()
db.refresh(group)
return group
@router.get("/groups/{group_id}", response_model=PurchaseGroupResponse)
def get_group(
group_id: str,
db: Session = Depends(get_db)
):
def get_group(group_id: str, db: Session = Depends(get_db)):
"""获取认购群详情"""
group = db.query(PurchaseGroup).filter(PurchaseGroup.id == group_id).first()
if not group:
@ -120,11 +264,10 @@ def join_group(
current_user = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""加入认购群"""
"""申请加入认购群"""
if not current_user:
raise HTTPException(status_code=401, detail="请先登录")
# 检查群是否存在
group = db.query(PurchaseGroup).filter(PurchaseGroup.id == group_id).first()
if not group:
raise HTTPException(status_code=404, detail="认购群不存在")
@ -132,39 +275,47 @@ def join_group(
if group.status != "active":
raise HTTPException(status_code=400, detail="该认购群已结束")
# 检查是否已加入
existing = db.query(PurchaseMember).filter(
PurchaseMember.group_id == group_id,
PurchaseMember.user_id == current_user.f99_90_id
).first()
if existing:
raise HTTPException(status_code=400, detail="您已加入该认购群")
if existing.status == "approved":
raise HTTPException(status_code=400, detail="您已加入该认购群")
elif existing.status == "pending":
raise HTTPException(status_code=400, detail="您已申请加入,等待审批")
elif existing.status == "rejected":
existing.status = "pending"
existing.joined_at = datetime.now()
group.total_pending += 1
db.commit()
return {"message": "已重新申请,等待审批"}
# 加入
member = PurchaseMember(
group_id=group_id,
user_id=current_user.f99_90_id,
user_name=current_user.f01_01_name,
user_avatar=current_user.avatar,
role="member"
role="member",
status="pending"
)
db.add(member)
# 更新群成员数
group.total_members += 1
group.total_pending += 1
db.commit()
return {"message": "加入成功"}
return {"message": "申请成功,等待群主审批"}
@router.get("/groups/{group_id}/members")
@router.get("/groups/{group_id}/members", response_model=List[MemberResponse])
def get_group_members(
group_id: str,
current_user = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""获取认购群成员列表"""
members = db.query(PurchaseMember).filter(
PurchaseMember.group_id == group_id
PurchaseMember.group_id == group_id,
PurchaseMember.status == "approved"
).order_by(PurchaseMember.joined_at.asc()).all()
return [
@ -174,16 +325,14 @@ def get_group_members(
"user_name": m.user_name,
"user_avatar": m.user_avatar,
"role": m.role,
"status": m.status,
"joined_at": m.joined_at
}
for m in members
]
@router.get("/groups/{group_id}/collections")
def get_group_collections(
group_id: str,
db: Session = Depends(get_db)
):
@router.get("/groups/{group_id}/collections", response_model=List[PurchaseCollectionResponse])
def get_group_collections(group_id: str, db: Session = Depends(get_db)):
"""获取认购群藏品列表"""
collections = db.query(PurchaseCollection).filter(
PurchaseCollection.group_id == group_id
@ -192,19 +341,25 @@ def get_group_collections(
return [
{
"id": c.id,
"group_id": c.group_id,
"user_id": c.user_id,
"user_name": c.user_name,
"collection_name": c.collection_name,
"collection_code": c.collection_code,
"number": c.number,
"grading": c.grading,
"boss_name": c.boss_name,
"price": float(c.price),
"source": c.source,
"paid": c.paid,
"status": c.status,
"submit_date": c.submit_date,
"submitted_at": c.submitted_at
}
for c in collections
]
@router.post("/groups/{group_id}/collections")
@router.post("/groups/{group_id}/collections", response_model=PurchaseCollectionResponse)
def add_collection(
group_id: str,
data: PurchaseCollectionCreate,
@ -215,35 +370,63 @@ def add_collection(
if not current_user:
raise HTTPException(status_code=401, detail="请先登录")
# 检查群是否存在
if not data.number:
raise HTTPException(status_code=400, detail="冠字号必填")
if not data.grading:
raise HTTPException(status_code=400, detail="评级必填")
if not data.boss_name:
raise HTTPException(status_code=400, detail="认购老板必填")
if not data.price:
raise HTTPException(status_code=400, detail="价格必填")
if not data.source:
raise HTTPException(status_code=400, detail="来源必填")
group = db.query(PurchaseGroup).filter(PurchaseGroup.id == group_id).first()
if not group:
raise HTTPException(status_code=404, detail="认购群不存在")
# 检查是否已加入
member = db.query(PurchaseMember).filter(
PurchaseMember.group_id == group_id,
PurchaseMember.user_id == current_user.f99_90_id
PurchaseMember.user_id == current_user.f99_90_id,
PurchaseMember.status == "approved"
).first()
if not member:
raise HTTPException(status_code=400, detail="请先加入该认购群")
raise HTTPException(status_code=400, detail="请先加入该认购群并通过审批")
max_code = db.query(PurchaseCollection).filter(
PurchaseCollection.group_id == group_id
).order_by(PurchaseCollection.collection_code.desc()).first()
if max_code and max_code.collection_code:
try:
next_num = int(max_code.collection_code) + 1
except:
next_num = 1
else:
next_num = 1
collection_code = f"{next_num:03d}"
# 添加藏品
collection = PurchaseCollection(
group_id=group_id,
user_id=current_user.f99_90_id,
user_name=current_user.f01_01_name,
collection_name=data.collection_name,
collection_code=data.collection_code,
collection_code=collection_code,
number=data.number,
price=data.price
grading=data.grading,
boss_name=data.boss_name,
price=data.price,
source=data.source,
paid=data.paid,
status="pending"
)
db.add(collection)
# 更新群统计
group.total_collections += 1
group.total_amount += float(data.price)
from decimal import Decimal
group.total_amount += Decimal(str(data.price))
if group.total_collections > 0:
group.avg_price = group.total_amount / group.total_collections
@ -252,13 +435,77 @@ def add_collection(
return collection
@router.put("/groups/{group_id}/collections/{collection_id}", response_model=PurchaseCollectionResponse)
def update_collection(
group_id: str,
collection_id: str,
data: PurchaseCollectionUpdate,
current_user = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""编辑认购藏品 (仅群主可操作)"""
if not current_user:
raise HTTPException(status_code=401, detail="请先登录")
group = db.query(PurchaseGroup).filter(PurchaseGroup.id == group_id).first()
if not group:
raise HTTPException(status_code=404, detail="认购群不存在")
if group.creator_id != current_user.f99_90_id:
raise HTTPException(status_code=403, detail="只有群主可以编辑")
collection = db.query(PurchaseCollection).filter(
PurchaseCollection.id == collection_id,
PurchaseCollection.group_id == group_id
).first()
if not collection:
raise HTTPException(status_code=404, detail="藏品不存在")
old_price = float(collection.price)
# 更新字段
if data.collection_name is not None:
collection.collection_name = data.collection_name
if data.number is not None:
collection.number = data.number
if data.grading is not None:
collection.grading = data.grading
if data.boss_name is not None:
collection.boss_name = data.boss_name
if data.source is not None:
collection.source = data.source
if data.paid is not None:
collection.paid = data.paid
# 处理状态变化
new_status = data.status
if new_status and new_status != collection.status:
if new_status == "confirmed" and collection.status != "confirmed":
group.total_confirmed += 1
elif collection.status == "confirmed" and new_status != "confirmed":
group.total_confirmed -= 1
collection.status = new_status
# 处理价格变化
if data.price is not None and data.price != old_price:
from decimal import Decimal
group.total_amount = group.total_amount - Decimal(str(old_price)) + Decimal(str(data.price))
if group.total_collections > 0:
group.avg_price = group.total_amount / group.total_collections
db.commit()
db.refresh(collection)
return collection
@router.post("/groups/{group_id}/close")
def close_group(
group_id: str,
current_user = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""结束认购群(仅群主可操作)"""
"""结束认购群"""
if not current_user:
raise HTTPException(status_code=401, detail="请先登录")
@ -266,7 +513,6 @@ def close_group(
if not group:
raise HTTPException(status_code=404, detail="认购群不存在")
# 检查是否为群主
if group.creator_id != current_user.f99_90_id:
raise HTTPException(status_code=403, detail="只有群主可以结束认购群")
@ -274,3 +520,123 @@ def close_group(
db.commit()
return {"message": "认购群已结束"}
@router.delete("/groups/{group_id}")
def delete_group(
group_id: str,
current_user = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""删除认购群 (仅管理员可操作)"""
if not current_user:
raise HTTPException(status_code=401, detail="请先登录")
# 管理员权限检查
if current_user.role not in ["admin", "superadmin"]:
raise HTTPException(status_code=403, detail="只有管理员可以删除认购群")
group = db.query(PurchaseGroup).filter(PurchaseGroup.id == group_id).first()
if not group:
raise HTTPException(status_code=404, detail="认购群不存在")
# 删除相关成员和藏品
db.query(PurchaseMember).filter(PurchaseMember.group_id == group_id).delete()
db.query(PurchaseCollection).filter(PurchaseCollection.group_id == group_id).delete()
db.delete(group)
db.commit()
return {"message": "认购群已删除"}
class MemberUpdate(BaseModel):
wechat_name: Optional[str] = None
contact: Optional[str] = None
purchase_count: Optional[int] = None
paid_amount: Optional[float] = None
notes: Optional[str] = None
class GroupUpdate(BaseModel):
name: Optional[str] = None
description: Optional[str] = None
cycle_months: Optional[int] = None
start_date: Optional[str] = None
# 更新成员信息
@router.put("/groups/{group_id}/members/{member_id}")
def update_member(
group_id: str,
member_id: str,
data: MemberUpdate,
current_user = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""更新成员信息 (成员自己或群主可编辑)"""
if not current_user:
raise HTTPException(status_code=401, detail="请先登录")
group = db.query(PurchaseGroup).filter(PurchaseGroup.id == group_id).first()
if not group:
raise HTTPException(status_code=404, detail="认购群不存在")
member = db.query(PurchaseMember).filter(
PurchaseMember.id == member_id,
PurchaseMember.group_id == group_id
).first()
if not member:
raise HTTPException(status_code=404, detail="成员不存在")
# 只有成员自己或群主可以修改
if member.user_id != current_user.f99_90_id and group.creator_id != current_user.f99_90_id:
raise HTTPException(status_code=403, detail="无权修改")
if data.wechat_name is not None:
member.wechat_name = data.wechat_name
if data.contact is not None:
member.contact = data.contact
if data.purchase_count is not None:
member.purchase_count = data.purchase_count
if data.paid_amount is not None:
from decimal import Decimal
member.paid_amount = Decimal(str(data.paid_amount))
if data.notes is not None:
member.notes = data.notes
db.commit()
db.refresh(member)
return {"message": "更新成功", "member_id": member.id}
# 更新群信息
@router.put("/groups/{group_id}", response_model=PurchaseGroupResponse)
def update_group(
group_id: str,
data: GroupUpdate,
current_user = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""更新群信息 (仅群主可编辑)"""
if not current_user:
raise HTTPException(status_code=401, detail="请先登录")
group = db.query(PurchaseGroup).filter(PurchaseGroup.id == group_id).first()
if not group:
raise HTTPException(status_code=404, detail="认购群不存在")
if group.creator_id != current_user.f99_90_id:
raise HTTPException(status_code=403, detail="只有群主可以修改")
if data.name is not None:
group.name = data.name
if data.description is not None:
group.description = data.description
if data.cycle_months is not None:
group.cycle_months = data.cycle_months
if data.start_date is not None:
try:
group.start_date = datetime.fromisoformat(data.start_date.replace('Z', '+08:00'))
except:
pass
db.commit()
db.refresh(group)
return group

View File

@ -1,5 +1,28 @@
# 版本更新记录
## v0.0.12 (2026-04-29)
### 前端更新
- **Purchase.jsx**
- 完善:入群申请审批流程 (待审批成员列表)
- 完善:新增认购藏品字段
- 编号自动生成001开始
- 冠字号:必填
- 评级:必填 (下拉选择)
- 认购老板:必填 (从成员中选择)
- 价格:必填
- 来源:必填 (一尘/直播/群友/其他)
- 认购款结清checkbox
- 优化:表单布局
### 后端更新
- **purchase.py**
- 新增:入群审批接口 /approve
- 新增:待审批成员列表 /pending-members
- 新增:认购藏品新字段 (grading, boss_name, source, paid, submit_date)
---
## v0.0.7 (2026-04-24)
### 代码清理

View File

@ -1,15 +1,15 @@
{
"version": "0.0.9",
"version": "0.0.12",
"updated": "2026-04-24",
"modules": {
"frontend": {
"version": "0.0.9",
"version": "0.0.12",
"pages": {
"Add": "0.0.1",
"Admin": "0.0.1",
"Detail": "0.0.1",
"Edit": "0.0.1",
"Home": "0.0.4",
"Home": "0.0.7",
"List": "0.0.1",
"Login": "0.0.1",
"News": "0.0.8",
@ -23,7 +23,7 @@
}
},
"backend": {
"version": "0.0.9",
"version": "0.0.12",
"routers": {
"auth": "0.0.1",
"collections": "0.0.1",

View File

@ -226,7 +226,7 @@ export default function Home() {
<div style={{ color: '#f59e0b', fontSize: '18px', fontWeight: '600', whiteSpace: 'nowrap' }}>发布寻号</div>
<div style={{ color: 'rgba(255,255,255,0.5)', fontSize: '10px', marginTop: '2px' }}>发布寻配需求</div>
</div>
<div onClick={() => window.location.hash = '#/add?mode=manual'} style={{
<div onClick={() => window.location.hash = '#/purchase'} style={{
background: 'linear-gradient(135deg, rgba(255,255,255,0.08) 0%, rgba(255,255,255,0.02) 100%)',
borderRadius: '12px',
padding: '12px 16px',
@ -238,8 +238,8 @@ export default function Home() {
justifyContent: 'center',
textAlign: 'center'
}}>
<div style={{ color: '#3b82f6', fontSize: '18px', fontWeight: '600', whiteSpace: 'nowrap' }}>发布藏品</div>
<div style={{ color: 'rgba(255,255,255,0.5)', fontSize: '10px', marginTop: '2px' }}>手动发布</div>
<div style={{ color: '#ec4899', fontSize: '18px', fontWeight: '600', whiteSpace: 'nowrap' }}>我的认购</div>
<div style={{ color: 'rgba(255,255,255,0.5)', fontSize: '10px', marginTop: '2px' }}>认购群入口</div>
</div>
</div>
</div>
@ -407,14 +407,16 @@ export default function Home() {
{(isAdmin ? [
{ icon: '🏠', label: '首页', hash: '#/', idx: 0 },
{ icon: '📚', label: '藏品', hash: '#/list', idx: 1 },
{ icon: '', label: '录入', hash: '#/ocr', idx: 2 },
{ icon: '📊', label: '统计', hash: '#/stats', idx: 3 },
{ icon: '⚙️', label: '管理', hash: '#/admin', idx: 4 }
{ icon: '🎯', label: '认购', hash: '#/purchase', idx: 2 },
{ icon: '', label: '录入', hash: '#/ocr', idx: 3 },
{ icon: '📊', label: '统计', hash: '#/stats', idx: 4 },
{ icon: '⚙️', label: '管理', hash: '#/admin', idx: 5 }
] : [
{ icon: '🏠', label: '首页', hash: '#/', idx: 0 },
{ icon: '📚', label: '藏品', hash: '#/list', idx: 1 },
{ icon: '', label: '录入', hash: '#/ocr', idx: 2 },
{ icon: '📊', label: '统计', hash: '#/stats', idx: 3 }
{ icon: '🎯', label: '认购', hash: '#/purchase', idx: 2 },
{ icon: '', label: '录入', hash: '#/ocr', idx: 3 },
{ icon: '📊', label: '统计', hash: '#/stats', idx: 4 }
]).map((item) => (
<div key={item.idx} onClick={() => window.location.hash = item.hash} style={{
display: 'flex',

View File

@ -17,6 +17,7 @@ function Purchase() {
const [selectedGroup, setSelectedGroup] = useState(null)
const [groupMembers, setGroupMembers] = useState([])
const [groupCollections, setGroupCollections] = useState([])
const [showCollectionForm, setShowCollectionForm] = useState(false)
const [newGroup, setNewGroup] = useState({ name: '', description: '' })
const [newCollection, setNewCollection] = useState({ collection_name: '', collection_code: '', number: '', price: '' })
const [loading, setLoading] = useState(true)
@ -247,7 +248,7 @@ function Purchase() {
if (selectedGroup) {
return (
<div style={{
minHeight: '100vh',
minHeight: '100vh', overflowY: 'auto', WebkitOverflowScrolling: 'touch',
background: '#0f172a',
color: '#fff',
padding: '16px'
@ -369,15 +370,16 @@ function Purchase() {
</div>
))}
{groupCollections.length === 0 && <div style={{ color: 'rgba(255,255,255,0.3)', fontSize: '13px', textAlign: 'center', padding: '20px' }}>暂无认购藏品</div>}
{selectedGroup && user && selectedGroup.creator_id === user.id && selectedGroup.status === 'active' && (
<button onClick={() => setShowCollectionForm(!showCollectionForm)} style={{ marginTop: '12px', width: '100%', background: showCollectionForm ? '#ef4444' : '#3b82f6', color: '#fff', border: 'none', borderRadius: '8px', padding: '12px', fontSize: '14px', fontWeight: 'bold', cursor: 'pointer' }}>{showCollectionForm ? '收起' : '录入认购藏品'}</button>
)}
</div>
{/* 提交认购藏品表单 */}
{selectedGroup.status === 'active' && (
{/* 提交认购藏品表单 - 仅群主可提交 */}
{selectedGroup && user && selectedGroup.creator_id === user.id && selectedGroup.status === 'active' && showCollectionForm && (
<div style={{
position: 'fixed',
bottom: '20px',
left: '16px',
right: '16px',
marginTop: '16px',
marginBottom: '80px',
background: '#1e293b',
borderRadius: '12px',
padding: '16px',
@ -459,13 +461,20 @@ function Purchase() {
</button>
</div>
)}
{/* 非群主提示 */}
{selectedGroup && user && !(selectedGroup.creator_id === user.id) && selectedGroup.status === 'active' && (
<div style={{ marginTop: '16px', marginBottom: '80px', background: '#1e293b', borderRadius: '12px', padding: '16px', textAlign: 'center', color: 'rgba(255,255,255,0.5)' }}>
仅群主可提交认购藏品
</div>
)}
</div>
)
}
return (
<div style={{
minHeight: '100vh',
minHeight: '100vh', overflowY: 'auto', WebkitOverflowScrolling: 'touch',
background: '#0f172a',
color: '#fff',
padding: '16px'