Files
qiji/backend/app/services/excel_export.py
T
v6ole e7af0e15ab feat: 完整功能迭代 — 移动端+PC端全面优化
新增:
- 今日纪要 (6分类+彩色标签+时间选择器)
- 客户经理PC端工作台 (我的数据,5模块CRUD)
- 用户管理页 (支局长修改角色)
- 客户备注/收支费用(金额+单位)/联系人管理
- 拜访人姓名+电话字段
- 客户导入导出/旧周报导入模板下载
- 序号列+分页(25/50/100)

修复/优化:
- Hash路由→HTML5 History, Casdoor回调正常
- 时区统一为Asia/Shanghai (today_cst)
- 数据库懒加载→selectinload预加载
- 日期解析兼容ISO datetime字符串
- 文件上传定位修复 (position:relative)
- 表单button type='button'防止误提交
- 分管领导权限(只读客户档案,不可编辑)
- 侧边栏折叠+SVG图标
- 拜访方式/紧急度统一chip风格
- 时间范围统一为el-time-picker(is-range)
- 全局CSS设计变量+box-sizing修复滚动条

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-24 08:30:12 +08:00

140 lines
5.2 KiB
Python

import io
from datetime import date, timedelta
from openpyxl import Workbook
from openpyxl.styles import Font, Alignment, Border, Side
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from app.models.visit import Visit
from app.models.work_plan import WorkPlan
from app.models.mini_business import MiniBusiness
from app.models.key_visit import KeyVisit
from app.models.customer import Customer
from app.models.user import User
def get_week_range(reference_date: date | None = None):
"""Get the Monday and Sunday of the week containing reference_date (defaults to today)."""
today = reference_date or date.today()
monday = today - timedelta(days=today.weekday())
sunday = monday + timedelta(days=6)
return monday, sunday
async def export_weekly_report(db: AsyncSession, reference_date: date | None = None) -> io.BytesIO:
"""Generate a 4-sheet xlsx matching the existing weekly report template."""
monday, sunday = get_week_range(reference_date)
wb = Workbook()
# Pre-fetch lookups
customers_result = await db.execute(select(Customer.id, Customer.name))
customer_map = {str(c.id): c.name for c in customers_result.all()}
users_result = await db.execute(select(User.id, User.name))
user_map = {str(u.id): u.name for u in users_result.all()}
thin_border = Border(
left=Side(style='thin'), right=Side(style='thin'),
top=Side(style='thin'), bottom=Side(style='thin')
)
header_font = Font(bold=True)
# ── Sheet 1: 每日拜访记录 ──
ws1 = wb.active
ws1.title = "每日拜访记录"
headers1 = ["客户单位", "拜访日期", "拜访方式", "时间范围", "拜访人姓名", "拜访人电话", "沟通内容", "客户需求", "同访人员", "客户经理"]
ws1.append(headers1)
for col in range(1, len(headers1) + 1):
cell = ws1.cell(row=1, column=col)
cell.font = header_font
cell.border = thin_border
visits_result = await db.execute(
select(Visit).where(Visit.visit_date >= monday, Visit.visit_date <= sunday)
)
for v in visits_result.scalars():
companions_names = [user_map.get(str(cid), str(cid)) for cid in (v.companions or [])]
ws1.append([
customer_map.get(str(v.customer_id), ""),
str(v.visit_date),
v.visit_method,
v.time_range,
v.visitor_name or "",
v.visitor_phone or "",
v.communication_content,
v.customer_demand,
", ".join(companions_names),
user_map.get(str(v.manager_id), ""),
])
# ── Sheet 2: 下周工作计划 ──
ws2 = wb.create_sheet("下周工作计划")
headers2 = ["客户单位", "工作计划", "计划拜访时间", "客户经理", "状态"]
ws2.append(headers2)
for col in range(1, len(headers2) + 1):
cell = ws2.cell(row=1, column=col)
cell.font = header_font
cell.border = thin_border
plans_result = await db.execute(select(WorkPlan))
for p in plans_result.scalars():
ws2.append([
customer_map.get(str(p.customer_id), ""),
p.plan_content,
str(p.plan_date),
user_map.get(str(p.manager_id), ""),
p.status,
])
# ── Sheet 3: 小微业务商机 ──
ws3 = wb.create_sheet("小微业务商机")
headers3 = ["客户单位", "产品类型", "金额", "跟进内容具体情况", "跟进状态", "客户经理", "预计列收时间"]
ws3.append(headers3)
for col in range(1, len(headers3) + 1):
cell = ws3.cell(row=1, column=col)
cell.font = header_font
cell.border = thin_border
mb_result = await db.execute(select(MiniBusiness))
for m in mb_result.scalars():
ws3.append([
customer_map.get(str(m.customer_id), ""),
m.product_type,
m.amount,
m.follow_up_detail,
m.status,
user_map.get(str(m.manager_id), ""),
m.expected_revenue_date,
])
# ── Sheet 4: 要客拜访计划 ──
ws4 = wb.create_sheet("要客拜访计划")
headers4 = ["客户单位", "紧急重要度", "内容描述", "进展状态", "计划拜访时间", "计划拜访人", "拜访对象", "客户经理"]
ws4.append(headers4)
for col in range(1, len(headers4) + 1):
cell = ws4.cell(row=1, column=col)
cell.font = header_font
cell.border = thin_border
kv_result = await db.execute(select(KeyVisit))
for k in kv_result.scalars():
ws4.append([
customer_map.get(str(k.customer_id), ""),
k.urgency_level,
k.description,
k.progress_status,
k.planned_date,
k.planned_visitor,
k.visit_target,
user_map.get(str(k.manager_id), ""),
])
# Adjust column widths
for ws in [ws1, ws2, ws3, ws4]:
for col_cells in ws.columns:
max_length = max((len(str(cell.value or "")) for cell in col_cells), default=10)
ws.column_dimensions[col_cells[0].column_letter].width = min(max_length + 4, 50)
output = io.BytesIO()
wb.save(output)
output.seek(0)
return output