feat: 亮灯表重构 — 点击卡片弹窗替代跳转周报

后端 light_board.py:
- 每张客户卡片返回 plans[] (活跃工作计划) + recent_visits[] (最近2条)
- 计划过期检测 (plan_overdue)

前端 LightBoard.vue:
- 点击卡片 → el-dialog 弹窗 (不再跳转周报)
- 绿灯: 显示最近拜访记录
- 黄/红灯: 客户信息 + 快速制定拜访计划表单
- 卡片显示计划标签 (📅正常 / ⚠过期闪烁)
- 弹窗底部保留「查看周报记录」链接

计划过期逻辑: plan_date < 当月首日 且 status=计划中

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-06-27 12:23:58 +08:00
parent 2bb85bf45c
commit 0e25201ede
2 changed files with 266 additions and 316 deletions
+54
View File
@@ -15,6 +15,7 @@ from app.models.user import User
from app.models.customer import Customer
from app.models.customer_assignment import CustomerAssignment
from app.models.visit import Visit
from app.models.work_plan import WorkPlan
from app.utils.timezone import today_cst
@@ -156,6 +157,59 @@ async def get_light_board(
else:
mgr_entry["not_visited_2months"] += 1
# ── Active work plans per customer ──
active_plans_result = await db.execute(
select(
WorkPlan.customer_id,
WorkPlan.plan_content,
WorkPlan.plan_date,
WorkPlan.status,
WorkPlan.manager_id,
).where(
WorkPlan.customer_id.in_(assigned_customer_ids),
WorkPlan.status == "计划中",
).order_by(WorkPlan.plan_date)
)
plans_map: dict[UUID, list] = {}
for row in active_plans_result.all():
cid, content, pdate, pstatus, pmanager = row
plans_map.setdefault(cid, []).append({
"plan_content": content,
"plan_date": str(pdate),
"status": pstatus,
"plan_overdue": pdate < ref,
"manager_id": str(pmanager) if pmanager else None,
})
# ── Recent visits for green/yellow customers ──
recent_visits_result = await db.execute(
select(
Visit.customer_id,
Visit.visit_date,
Visit.visit_method,
Visit.communication_content,
Visit.manager_id,
).where(
Visit.customer_id.in_(assigned_customer_ids),
).order_by(Visit.visit_date.desc()).limit(500)
)
visits_map: dict[UUID, list] = {}
for row in recent_visits_result.all():
cid, vdate, vmethod, vcontent, vmanager = row
visits_map.setdefault(cid, []).append({
"visit_date": str(vdate),
"visit_method": vmethod,
"content": (vcontent or "")[:120],
"manager_id": str(vmanager) if vmanager else None,
})
# ── Merge plans & visits into customer entries ──
for mgr_entry in manager_map.values():
for c in mgr_entry["customers"]:
cid = UUID(c["customer_id"])
c["plans"] = plans_map.get(cid, [])
c["recent_visits"] = (visits_map.get(cid, []) or [])[:2]
# ── Calculate coverage rates ──
for mgr_entry in manager_map.values():
total = mgr_entry["total_customers"]