项目总览界面UI美化与入组数据统计修复
- 重构总览页面布局:卡片化设计、顶部标题栏、图标装饰 - 美化进度时间线:hover高亮、呼吸动画、渐变连接线 - 重写入组图表:去除边框、虚线网格、渐变柱子、柔和背景 - 修复入组统计:同时匹配enrollment_date和ENROLLED状态 - 移除adapter中hasEnrollmentStage过滤,显示真实入组数 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@ import uuid
|
||||
from datetime import datetime
|
||||
from typing import Dict, List, Tuple
|
||||
|
||||
from sqlalchemy import func, select, and_
|
||||
from sqlalchemy import Date, cast, func, select, and_, or_
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.models.site import Site
|
||||
@@ -86,20 +86,18 @@ async def get_project_overview(db: AsyncSession, study_id: uuid.UUID) -> Dict:
|
||||
startup_status = "NOT_STARTED"
|
||||
startup_completed = kickoff.get("kickoff_date") if contract_status == "COMPLETED" else None
|
||||
|
||||
# 入组状态 - 依赖启动会完成
|
||||
# 入组状态
|
||||
enrollment = enrollment_data.get(site_id_str, {"actual": 0})
|
||||
enrollment_actual = enrollment["actual"]
|
||||
enrollment_target = site.enrollment_target or 0
|
||||
|
||||
if startup_status == "COMPLETED":
|
||||
if enrollment_actual >= enrollment_target and enrollment_target > 0:
|
||||
enrollment_status = "COMPLETED"
|
||||
elif enrollment_actual > 0:
|
||||
enrollment_status = "IN_PROGRESS"
|
||||
else:
|
||||
enrollment_status = "NOT_STARTED"
|
||||
|
||||
if enrollment_actual >= enrollment_target and enrollment_target > 0:
|
||||
enrollment_status = "COMPLETED"
|
||||
elif enrollment_actual > 0:
|
||||
enrollment_status = "IN_PROGRESS"
|
||||
elif startup_status == "COMPLETED":
|
||||
enrollment_status = "NOT_STARTED"
|
||||
else:
|
||||
# 启动会未完成,入组强制为未开始
|
||||
enrollment_status = "NOT_STARTED"
|
||||
|
||||
# 检查状态 - 依赖入组完成
|
||||
@@ -265,35 +263,44 @@ async def _get_enrollment_data(db: AsyncSession, study_id: uuid.UUID, site_ids:
|
||||
and_(
|
||||
Subject.study_id == study_id,
|
||||
Subject.site_id.in_(site_ids),
|
||||
Subject.enrollment_date.isnot(None)
|
||||
or_(
|
||||
Subject.enrollment_date.isnot(None),
|
||||
Subject.status == "ENROLLED"
|
||||
)
|
||||
)
|
||||
).group_by(Subject.site_id)
|
||||
|
||||
|
||||
result = await db.execute(stmt)
|
||||
rows = result.all()
|
||||
|
||||
|
||||
data = {}
|
||||
for row in rows:
|
||||
data[str(row.site_id)] = {"actual": row.count}
|
||||
|
||||
|
||||
return data
|
||||
|
||||
|
||||
async def _get_monthly_enrollment(db: AsyncSession, study_id: uuid.UUID) -> List[Dict]:
|
||||
"""获取月度入组统计"""
|
||||
month_col = func.to_char(Subject.enrollment_date, 'YYYY-MM').label("month")
|
||||
|
||||
month_col = func.to_char(
|
||||
func.coalesce(Subject.enrollment_date, func.cast(Subject.created_at, Date)),
|
||||
'YYYY-MM'
|
||||
).label("month")
|
||||
|
||||
stmt = select(
|
||||
month_col,
|
||||
func.count(Subject.id).label("count")
|
||||
).where(
|
||||
and_(
|
||||
Subject.study_id == study_id,
|
||||
Subject.enrollment_date.isnot(None)
|
||||
or_(
|
||||
Subject.enrollment_date.isnot(None),
|
||||
Subject.status == "ENROLLED"
|
||||
)
|
||||
)
|
||||
).group_by(month_col).order_by(month_col)
|
||||
|
||||
|
||||
result = await db.execute(stmt)
|
||||
rows = result.all()
|
||||
|
||||
|
||||
return [{"month": row.month, "count": row.count} for row in rows]
|
||||
|
||||
Reference in New Issue
Block a user