Parcourir la source

增加导出功能

xueyiming il y a 2 jours
Parent
commit
24f63aeb0b
2 fichiers modifiés avec 54 ajouts et 0 suppressions
  1. 0 0
      app/utils/__init__.py
  2. 54 0
      app/utils/excel_export.py

+ 0 - 0
app/utils/__init__.py


+ 54 - 0
app/utils/excel_export.py

@@ -0,0 +1,54 @@
+"""将行数据序列化为 Excel 字节流。"""
+
+from __future__ import annotations
+
+import json
+from io import BytesIO
+from typing import Iterable
+from urllib.parse import quote
+
+from openpyxl import Workbook
+from openpyxl.utils import get_column_letter
+
+
+def _cell_value(raw: object) -> object:
+    if raw is None:
+        return ""
+    if isinstance(raw, (list, dict)):
+        return json.dumps(raw, ensure_ascii=False)
+    return raw
+
+
+def rows_to_excel_bytes(
+    rows: Iterable[dict[str, object]],
+    columns: list[tuple[str, str]],
+    *,
+    sheet_name: str = "Sheet1",
+) -> bytes:
+    workbook = Workbook()
+    worksheet = workbook.active
+    worksheet.title = sheet_name[:31]
+
+    headers = [header for header, _ in columns]
+    worksheet.append(headers)
+
+    for row in rows:
+        worksheet.append([_cell_value(row.get(field)) for _, field in columns])
+
+    for index, (header, _) in enumerate(columns, start=1):
+        column_letter = get_column_letter(index)
+        max_len = len(header)
+        for cell in worksheet[column_letter]:
+            if cell.value is not None:
+                max_len = max(max_len, len(str(cell.value)))
+        worksheet.column_dimensions[column_letter].width = min(max_len + 2, 60)
+
+    buffer = BytesIO()
+    workbook.save(buffer)
+    return buffer.getvalue()
+
+
+def build_content_disposition(filename: str) -> str:
+    ascii_fallback = "export.xlsx"
+    encoded = quote(filename, safe="")
+    return f"attachment; filename=\"{ascii_fallback}\"; filename*=UTF-8''{encoded}"