excel_util.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. import io
  2. from typing import Any
  3. import pandas as pd
  4. from openpyxl import Workbook
  5. from openpyxl.styles import Alignment, PatternFill
  6. from openpyxl.utils import get_column_letter
  7. from openpyxl.worksheet.datavalidation import DataValidation
  8. class ExcelUtil:
  9. """
  10. Excel 模板生成与列表导出(openpyxl / pandas)。
  11. """
  12. @classmethod
  13. def __mapping_list(cls, list_data: list[dict[str, Any]], mapping_dict: dict) -> list:
  14. """
  15. 工具方法:将列表数据中的字段名映射为对应的中文字段名。
  16. 参数:
  17. - list_data (list[dict[str, Any]]): 数据列表。
  18. - mapping_dict (dict): 字段名映射字典。
  19. 返回:
  20. - list: 映射后的数据列表。
  21. """
  22. mapping_data = [
  23. {mapping_dict.get(key): item.get(key) for key in mapping_dict} for item in list_data
  24. ]
  25. return mapping_data
  26. @classmethod
  27. def get_excel_template(
  28. cls,
  29. header_list: list[str],
  30. selector_header_list: list[str],
  31. option_list: list[dict[str, list[str]]],
  32. ) -> bytes:
  33. """
  34. 生成 Excel 模板文件。
  35. 参数:
  36. - header_list (list[str]): 表头列表。
  37. - selector_header_list (list[str]): 需要设置下拉选择的表头列表。
  38. - option_list (list[dict[str, list[str]]]): 下拉选项配置列表。
  39. 返回:
  40. - bytes: Excel 文件的二进制数据。
  41. """
  42. wb = Workbook()
  43. ws = wb.active
  44. if not ws:
  45. raise ValueError("不存在活动工作表")
  46. # 设置表头样式
  47. header_fill = PatternFill(start_color="ababab", end_color="ababab", fill_type="solid")
  48. # 写入表头
  49. for col_num, header in enumerate(header_list, 1):
  50. cell = ws.cell(row=1, column=col_num)
  51. cell.value = header # pyright: ignore[reportAttributeAccessIssue]
  52. cell.fill = header_fill
  53. # 设置水平居中对齐
  54. cell.alignment = Alignment(horizontal="center")
  55. # 设置列宽度为16
  56. ws.column_dimensions[get_column_letter(col_num)].width = 12
  57. # 设置下拉选择
  58. for selector_header in selector_header_list:
  59. col_idx = header_list.index(selector_header) + 1
  60. # 获取当前表头的选项列表
  61. header_options = next(
  62. (opt.get(selector_header) for opt in option_list if selector_header in opt),
  63. [],
  64. )
  65. if header_options:
  66. dv = DataValidation(type="list", formula1=f'"{",".join(header_options)}"')
  67. dv.add(f"{get_column_letter(col_idx)}2:{get_column_letter(col_idx)}1048576")
  68. ws.add_data_validation(dv)
  69. # 导出为二进制数据
  70. buffer = io.BytesIO()
  71. wb.save(buffer)
  72. buffer.seek(0)
  73. # 读取字节数据
  74. excel_data = buffer.getvalue()
  75. return excel_data
  76. @classmethod
  77. def export_list2excel(cls, list_data: list[dict[str, Any]], mapping_dict: dict) -> bytes:
  78. """
  79. 将列表数据导出为 Excel 文件。
  80. 参数:
  81. - list_data (list[dict[str, Any]]): 要导出的数据列表。
  82. - mapping_dict (dict): 字段名映射字典。
  83. 返回:
  84. - bytes: Excel 文件的二进制数据。
  85. """
  86. mapping_data = cls.__mapping_list(list_data, mapping_dict)
  87. df = pd.DataFrame(mapping_data)
  88. buffer = io.BytesIO()
  89. df.to_excel(buffer, index=False, engine="openpyxl") # pyright: ignore[reportArgumentType]
  90. binary_data = buffer.getvalue()
  91. return binary_data