From cd47d1aed5acfda0eb07d7f4d7d7970032dd528e Mon Sep 17 00:00:00 2001 From: v6ole Date: Thu, 2 Jul 2026 16:58:29 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BA=AE=E7=81=AF=E8=A1=A830=E5=A4=A9?= =?UTF-8?q?=E6=BB=9A=E5=8A=A8=E7=AA=97=E5=8F=A3=20+=20=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E4=BA=BA=E5=91=98=E6=96=87=E6=A1=88=E7=BB=9F=E4=B8=80=20+=20?= =?UTF-8?q?=E5=91=A8=E6=8A=A5=E6=95=B0=E6=8D=AE=E8=A1=A5=E5=85=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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: 同访人员 → 相关人员 --- backend/app/api/import_data.py | 2 +- backend/app/services/dashboard.py | 8 ++++ backend/app/services/excel_export.py | 2 +- backend/app/services/light_board.py | 37 ++++++++---------- frontend/src/views/desktop/LightBoard.vue | 8 ++-- .../src/views/desktop/ManagerWorkspace.vue | 2 +- frontend/src/views/mobile/VisitForm.vue | 2 +- weekly_report_template (2)_补全.xlsx | Bin 0 -> 22924 bytes 8 files changed, 33 insertions(+), 28 deletions(-) create mode 100644 weekly_report_template (2)_补全.xlsx 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: '未分配' }