feat: 亮灯表30天滚动窗口 + 相关人员文案统一 + 周报数据补全

- light_board.classify: 自然月 → 30/60天滚动窗口 (≤30天🟢, 31-60天🟡, 61+天🔴)
- LightBoard.vue: 4处文案同步 (近30天已拜访 / 上次 / 个周期)
- dashboard.get_weekly_report: 补全 companion_names_resolved + visitor_name + visitor_phone + edit_log
- 导入/导出模板 + VisitForm + ManagerWorkspace: 同访人员 → 相关人员
This commit is contained in:
2026-07-02 16:58:29 +08:00
parent 3285e22142
commit cd47d1aed5
8 changed files with 33 additions and 28 deletions
+17 -20
View File
@@ -1,9 +1,9 @@
"""Customer light board — visit coverage matrix per manager.
Status:
green — visited this month (亮灯)
yellow — visited last month but not this month (临期)
red — not visited in 2+ months, or never visited (灭灯+警示)
Status (rolling 30-day window):
green — visited within last 30 days (亮灯)
yellow — visited 31-60 days ago (临期)
red — visited 61+ days ago, or never visited (灭灯+警示)
gray — customer has no assigned primary manager (未分配)
"""
@@ -29,29 +29,26 @@ def month_end(ref: date) -> date:
def classify(last_visit_date: date | None, ref: date) -> tuple[str, int]:
"""Return (status, consecutive_missed_months)."""
"""Return (status, consecutive_missed_periods).
30-day rolling window:
green — visited within 30 days
yellow — visited 31-60 days ago (临期)
red — 61+ days ago, or never visited
"""
if last_visit_date is None:
return ("red", 99) # never visited
this_month = month_start(ref)
last_month = month_start(this_month - timedelta(days=1))
days_since = (ref - last_visit_date).days
if last_visit_date >= this_month:
if days_since <= 30:
return ("green", 0)
elif last_visit_date >= last_month:
elif days_since <= 60:
return ("yellow", 0)
# Count how many consecutive months missed
cursor = month_start(ref)
missed = 0
while True:
cursor = month_start(cursor - timedelta(days=1))
if last_visit_date >= cursor:
break
missed += 1
if missed > 24: # safety cap
break
return ("red", missed)
# Each 30-day block beyond 60 days counts as one missed period
periods = days_since // 30
return ("red", min(periods, 99))
async def get_light_board(