4755cc75ba
- 管理后台用户/统计/日志/配置四页签全部对接真实后端API - auth注册/登录/游客/微信登录事件写入usage_logs表 - 提取信息结果从原始JSON改为卡片式字段列表(中文标签) - 管理后台搜索按钮增加加载态和结果数提示 - 配置WECHAT_APP_ID/WECHAT_APP_SECRET - 客户/产品/报价单CRUD页面完整(导出导入批量操作)
192 lines
6.9 KiB
Python
192 lines
6.9 KiB
Python
from typing import List, Dict, Any
|
|
import csv
|
|
import io
|
|
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
try:
|
|
import openpyxl
|
|
from openpyxl.styles import Font, PatternFill, Alignment
|
|
HAS_OPENPYXL = True
|
|
except ImportError:
|
|
HAS_OPENPYXL = False
|
|
|
|
|
|
def export_customers_csv(customers: List[Dict[str, Any]]) -> bytes:
|
|
output = io.StringIO()
|
|
writer = csv.writer(output)
|
|
writer.writerow(["Name", "Company", "Country", "Phone", "Email", "Status", "Last Contact"])
|
|
for c in customers:
|
|
writer.writerow([
|
|
c.get("name", ""),
|
|
c.get("company", ""),
|
|
c.get("country", ""),
|
|
c.get("phone", ""),
|
|
c.get("email", ""),
|
|
c.get("status", ""),
|
|
c.get("last_contact_at", ""),
|
|
])
|
|
return output.getvalue().encode("utf-8-sig")
|
|
|
|
|
|
def export_customers_xlsx(customers: List[Dict[str, Any]]) -> bytes:
|
|
if not HAS_OPENPYXL:
|
|
logger.warning("openpyxl not installed, falling back to CSV")
|
|
return export_customers_csv(customers)
|
|
|
|
wb = openpyxl.Workbook()
|
|
ws = wb.active
|
|
ws.title = "Customers"
|
|
|
|
headers = ["姓名", "公司", "国家", "电话", "WhatsApp", "邮箱", "状态", "最后联系"]
|
|
header_font = Font(bold=True, color="FFFFFF")
|
|
header_fill = PatternFill(start_color="1890FF", end_color="1890FF", fill_type="solid")
|
|
|
|
for col, h in enumerate(headers, 1):
|
|
cell = ws.cell(row=1, column=col, value=h)
|
|
cell.font = header_font
|
|
cell.fill = header_fill
|
|
cell.alignment = Alignment(horizontal="center")
|
|
|
|
for row, c in enumerate(customers, 2):
|
|
ws.cell(row=row, column=1, value=c.get("name", ""))
|
|
ws.cell(row=row, column=2, value=c.get("company", ""))
|
|
ws.cell(row=row, column=3, value=c.get("country", ""))
|
|
ws.cell(row=row, column=4, value=c.get("phone", ""))
|
|
ws.cell(row=row, column=5, value=c.get("whatsapp_id", ""))
|
|
ws.cell(row=row, column=6, value=c.get("email", ""))
|
|
ws.cell(row=row, column=7, value=c.get("status", ""))
|
|
ws.cell(row=row, column=8, value=c.get("last_contact_at", ""))
|
|
|
|
ws.column_dimensions["A"].width = 20
|
|
ws.column_dimensions["B"].width = 25
|
|
ws.column_dimensions["C"].width = 15
|
|
ws.column_dimensions["D"].width = 18
|
|
ws.column_dimensions["E"].width = 18
|
|
ws.column_dimensions["F"].width = 30
|
|
ws.column_dimensions["G"].width = 12
|
|
ws.column_dimensions["H"].width = 20
|
|
|
|
output = io.BytesIO()
|
|
wb.save(output)
|
|
return output.getvalue()
|
|
|
|
|
|
def export_quotations_csv(quotations: List[Dict[str, Any]]) -> bytes:
|
|
output = io.StringIO()
|
|
writer = csv.writer(output)
|
|
writer.writerow(["Title", "Customer", "Currency", "Subtotal", "Total", "Status", "Date"])
|
|
for q in quotations:
|
|
writer.writerow([
|
|
q.get("title", ""),
|
|
q.get("customer_name", ""),
|
|
q.get("currency", "USD"),
|
|
q.get("subtotal", 0),
|
|
q.get("total", 0),
|
|
q.get("status", ""),
|
|
q.get("created_at", ""),
|
|
])
|
|
return output.getvalue().encode("utf-8-sig")
|
|
|
|
|
|
def export_quotations_xlsx(quotations: List[Dict[str, Any]]) -> bytes:
|
|
if not HAS_OPENPYXL:
|
|
logger.warning("openpyxl not installed, falling back to CSV")
|
|
return export_quotations_csv(quotations)
|
|
|
|
wb = openpyxl.Workbook()
|
|
ws = wb.active
|
|
ws.title = "Quotations"
|
|
|
|
headers = ["标题", "客户", "货币", "小计", "总计", "状态", "日期"]
|
|
header_font = Font(bold=True, color="FFFFFF")
|
|
header_fill = PatternFill(start_color="722ED1", end_color="722ED1", fill_type="solid")
|
|
|
|
for col, h in enumerate(headers, 1):
|
|
cell = ws.cell(row=1, column=col, value=h)
|
|
cell.font = header_font
|
|
cell.fill = header_fill
|
|
cell.alignment = Alignment(horizontal="center")
|
|
|
|
for row, q in enumerate(quotations, 2):
|
|
ws.cell(row=row, column=1, value=q.get("title", ""))
|
|
ws.cell(row=row, column=2, value=q.get("customer_name", ""))
|
|
ws.cell(row=row, column=3, value=q.get("currency", "USD"))
|
|
ws.cell(row=row, column=4, value=float(q.get("subtotal", 0)))
|
|
ws.cell(row=row, column=5, value=float(q.get("total", 0)))
|
|
ws.cell(row=row, column=6, value=q.get("status", ""))
|
|
ws.cell(row=row, column=7, value=str(q.get("created_at", "")))
|
|
|
|
ws.column_dimensions["A"].width = 30
|
|
ws.column_dimensions["B"].width = 20
|
|
ws.column_dimensions["C"].width = 10
|
|
ws.column_dimensions["D"].width = 15
|
|
ws.column_dimensions["E"].width = 15
|
|
ws.column_dimensions["F"].width = 12
|
|
ws.column_dimensions["G"].width = 20
|
|
|
|
output = io.BytesIO()
|
|
wb.save(output)
|
|
return output.getvalue()
|
|
|
|
|
|
def export_products_csv(products: List[Dict[str, Any]]) -> bytes:
|
|
output = io.StringIO()
|
|
writer = csv.writer(output)
|
|
writer.writerow(["名称", "英文名", "分类", "描述", "价格", "货币", "MOQ", "关键词"])
|
|
for p in products:
|
|
writer.writerow([
|
|
p.get("name", ""),
|
|
p.get("name_en", ""),
|
|
p.get("category", ""),
|
|
p.get("description", ""),
|
|
p.get("price", ""),
|
|
p.get("price_unit", "USD"),
|
|
p.get("moq", ""),
|
|
", ".join(p.get("keywords", [])),
|
|
])
|
|
return output.getvalue().encode("utf-8-sig")
|
|
|
|
|
|
def export_products_xlsx(products: List[Dict[str, Any]]) -> bytes:
|
|
if not HAS_OPENPYXL:
|
|
logger.warning("openpyxl not installed, falling back to CSV")
|
|
return export_products_csv(products)
|
|
|
|
wb = openpyxl.Workbook()
|
|
ws = wb.active
|
|
ws.title = "Products"
|
|
|
|
headers = ["名称", "英文名", "分类", "描述", "价格", "货币", "MOQ", "关键词"]
|
|
header_font = Font(bold=True, color="FFFFFF")
|
|
header_fill = PatternFill(start_color="07C160", end_color="07C160", fill_type="solid")
|
|
|
|
for col, h in enumerate(headers, 1):
|
|
cell = ws.cell(row=1, column=col, value=h)
|
|
cell.font = header_font
|
|
cell.fill = header_fill
|
|
cell.alignment = Alignment(horizontal="center")
|
|
|
|
for row, p in enumerate(products, 2):
|
|
ws.cell(row=row, column=1, value=p.get("name", ""))
|
|
ws.cell(row=row, column=2, value=p.get("name_en", ""))
|
|
ws.cell(row=row, column=3, value=p.get("category", ""))
|
|
ws.cell(row=row, column=4, value=p.get("description", ""))
|
|
ws.cell(row=row, column=5, value=p.get("price", ""))
|
|
ws.cell(row=row, column=6, value=p.get("price_unit", "USD"))
|
|
ws.cell(row=row, column=7, value=p.get("moq", ""))
|
|
ws.cell(row=row, column=8, value=", ".join(p.get("keywords", [])))
|
|
|
|
ws.column_dimensions["A"].width = 20
|
|
ws.column_dimensions["B"].width = 20
|
|
ws.column_dimensions["C"].width = 15
|
|
ws.column_dimensions["D"].width = 40
|
|
ws.column_dimensions["E"].width = 12
|
|
ws.column_dimensions["F"].width = 10
|
|
ws.column_dimensions["G"].width = 12
|
|
ws.column_dimensions["H"].width = 30
|
|
|
|
output = io.BytesIO()
|
|
wb.save(output)
|
|
return output.getvalue() |