diff --git a/backend/app/api/import_data.py b/backend/app/api/import_data.py index e8f7821..30e2eef 100644 --- a/backend/app/api/import_data.py +++ b/backend/app/api/import_data.py @@ -22,7 +22,7 @@ async def download_weekly_report_template(): # Sheet 1: 每日拜访记录 ws1 = wb.active ws1.title = "每日拜访记录" - ws1.append(["客户单位", "拜访日期", "拜访方式", "时间范围", "拜访人姓名", "拜访人电话", "沟通内容", "客户需求", "同访人员", "客户经理"]) + ws1.append(["客户单位", "拜访日期", "拜访方式", "时间范围", "拜访人姓名", "拜访人电话", "沟通内容", "客户需求", "相关人员", "客户经理"]) for c in ws1[1]: c.font = header_font ws1.append(["XX科技有限公司", "2026-06-23", "上门", "9:00-10:00", "韦柳柏", "13800000000", "沟通了解云桌面需求", "希望扩容", "韦柳柏, 张科长", "韦矍森"]) ws1.column_dimensions['A'].width = 20; ws1.column_dimensions['E'].width = 30; ws1.column_dimensions['F'].width = 20 diff --git a/backend/app/services/dashboard.py b/backend/app/services/dashboard.py index ed109d5..e57b80c 100644 --- a/backend/app/services/dashboard.py +++ b/backend/app/services/dashboard.py @@ -138,6 +138,9 @@ async def get_weekly_report( visits_data = [] for v in visits: + # Resolve companion names: system users → names, external → direct + companion_names_resolved = [user_map.get(c, str(c)) for c in (v.companions or [])] + companion_names_resolved.extend(v.companion_names or []) visits_data.append({ "id": str(v.id), "customer_id": str(v.customer_id), @@ -145,12 +148,17 @@ async def get_weekly_report( "visit_date": str(v.visit_date), "visit_method": v.visit_method, "time_range": v.time_range, + "visitor_name": v.visitor_name or "", + "visitor_phone": v.visitor_phone or "", "communication_content": v.communication_content, "customer_demand": v.customer_demand, "companions": [str(c) for c in (v.companions or [])], + "companion_names": v.companion_names or [], + "companion_names_resolved": companion_names_resolved, "photos": v.photos or [], "manager_id": str(v.manager_id), "manager_name": user_map.get(v.manager_id, ""), + "edit_log": v.edit_log or [], "created_at": str(v.created_at), }) diff --git a/backend/app/services/excel_export.py b/backend/app/services/excel_export.py index d1ce2cd..5301f54 100644 --- a/backend/app/services/excel_export.py +++ b/backend/app/services/excel_export.py @@ -41,7 +41,7 @@ async def export_weekly_report(db: AsyncSession, reference_date: date | None = N # ── Sheet 1: 每日拜访记录 ── ws1 = wb.active ws1.title = "每日拜访记录" - headers1 = ["客户单位", "拜访日期", "拜访方式", "时间范围", "拜访人姓名", "拜访人电话", "沟通内容", "客户需求", "同访人员", "客户经理"] + headers1 = ["客户单位", "拜访日期", "拜访方式", "时间范围", "拜访人姓名", "拜访人电话", "沟通内容", "客户需求", "相关人员", "客户经理"] ws1.append(headers1) for col in range(1, len(headers1) + 1): cell = ws1.cell(row=1, column=col) diff --git a/backend/app/services/light_board.py b/backend/app/services/light_board.py index eb255ed..bc78c55 100644 --- a/backend/app/services/light_board.py +++ b/backend/app/services/light_board.py @@ -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( diff --git a/frontend/src/views/desktop/LightBoard.vue b/frontend/src/views/desktop/LightBoard.vue index 74d470b..cb99f2b 100644 --- a/frontend/src/views/desktop/LightBoard.vue +++ b/frontend/src/views/desktop/LightBoard.vue @@ -102,7 +102,7 @@ function goWeeklyReport(customerId: string) { } // Status label map -const statusLabel: Record = { green: '本月已拜访', yellow: '上月已拜访 (临期)', red: '急需拜访', gray: '未分配' } +const statusLabel: Record = { green: '近30天已拜访', yellow: '31-60天前 (临期)', red: '急需拜访', gray: '未分配' }