项目初次提交
This commit is contained in:
7
utils/excel/__init__.py
Normal file
7
utils/excel/__init__.py
Normal file
@ -0,0 +1,7 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2022/11/14 9:56
|
||||
# @File : __init__.py.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 简要说明
|
245
utils/excel/excel_manage.py
Normal file
245
utils/excel/excel_manage.py
Normal file
@ -0,0 +1,245 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2022/5/6 17:25
|
||||
# @File : excel_manage.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : EXCEL 文件操作
|
||||
|
||||
import datetime
|
||||
import os
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
from openpyxl.utils import get_column_letter
|
||||
from openpyxl import load_workbook, Workbook
|
||||
from application.settings import STATIC_ROOT, STATIC_URL
|
||||
from openpyxl.styles import Alignment, Font, PatternFill, Border, Side
|
||||
from utils.file.file_base import FileBase
|
||||
from .excel_schema import AlignmentModel, FontModel, PatternFillModel
|
||||
|
||||
|
||||
class ExcelManage:
|
||||
"""
|
||||
excel 文件序列化
|
||||
"""
|
||||
|
||||
# 列名,A-Z
|
||||
EXCEL_COLUMNS = [chr(a) for a in range(ord('A'), ord('Z') + 1)]
|
||||
|
||||
def __init__(self):
|
||||
self.sheet = None
|
||||
self.wb = None
|
||||
|
||||
def open_workbook(self, file: str, read_only: bool = False, data_only: bool = False) -> None:
|
||||
"""
|
||||
初始化 excel 文件
|
||||
:param file: 文件名称或者对象
|
||||
:param read_only: 是否只读,优化读取速度
|
||||
:param data_only: 是否加载文件对象
|
||||
:return:
|
||||
"""
|
||||
# 加载excel文件,获取表单
|
||||
self.wb = load_workbook(file, read_only=read_only, data_only=data_only)
|
||||
|
||||
def open_sheet(
|
||||
self,
|
||||
sheet_name: str = None,
|
||||
file: str = None,
|
||||
read_only: bool = False,
|
||||
data_only: bool = False
|
||||
) -> None:
|
||||
"""
|
||||
初始化 excel 文件
|
||||
:param sheet_name: 表单名称,为空则默认第一个
|
||||
:param file:
|
||||
:param read_only:
|
||||
:param data_only:
|
||||
:return:
|
||||
"""
|
||||
# 加载excel文件,获取表单
|
||||
if not self.wb:
|
||||
self.open_workbook(file, read_only, data_only)
|
||||
if sheet_name:
|
||||
if sheet_name in self.get_sheets():
|
||||
self.sheet = self.wb[sheet_name]
|
||||
else:
|
||||
self.sheet = self.wb.create_sheet(sheet_name)
|
||||
else:
|
||||
self.sheet = self.wb.active
|
||||
|
||||
def get_sheets(self) -> list:
|
||||
"""
|
||||
读取所有工作区名称
|
||||
:return: 一维数组
|
||||
"""
|
||||
return self.wb.sheetnames
|
||||
|
||||
def create_excel(self, sheet_name: str = None) -> None:
|
||||
"""
|
||||
创建 excel 文件
|
||||
:param sheet_name: 表单名称,为空则默认第一个
|
||||
:return:
|
||||
"""
|
||||
# 加载excel文件,获取表单
|
||||
self.wb = Workbook()
|
||||
self.sheet = self.wb.active
|
||||
if sheet_name:
|
||||
self.sheet.title = sheet_name
|
||||
|
||||
def readlines(self, min_row: int = 1, min_col: int = 1, max_row: int = None, max_col: int = None) -> list:
|
||||
"""
|
||||
读取指定表单所有数据
|
||||
:param min_row: 最小行
|
||||
:param min_col: 最小列
|
||||
:param max_row: 最大行
|
||||
:param max_col: 最大列
|
||||
:return: 二维数组
|
||||
"""
|
||||
rows = self.sheet.iter_rows(min_row=min_row, min_col=min_col, max_row=max_row, max_col=max_col)
|
||||
result = []
|
||||
for row in rows:
|
||||
_row = []
|
||||
for cell in row:
|
||||
_row.append(cell.value)
|
||||
if any(_row):
|
||||
result.append(_row)
|
||||
return result
|
||||
|
||||
def get_header(self, row: int = 1, col: int = None, asterisk: bool = False) -> list:
|
||||
"""
|
||||
读取指定表单的表头(第一行数据)
|
||||
:param row: 指定行
|
||||
:param col: 最大列
|
||||
:param asterisk: 是否去除 * 号
|
||||
:return: 一维数组
|
||||
"""
|
||||
rows = self.sheet.iter_rows(min_row=row, max_col=col)
|
||||
result = []
|
||||
for row in rows:
|
||||
for cell in row:
|
||||
value = cell.value.replace("*", "").strip() if asterisk else cell.value.strip()
|
||||
result.append(value)
|
||||
break
|
||||
return result
|
||||
|
||||
def write_list(self, rows: list, header: list = None) -> None:
|
||||
"""
|
||||
写入 excel文件
|
||||
:param rows: 行数据集
|
||||
:param header: 表头
|
||||
:return:
|
||||
"""
|
||||
if header:
|
||||
self.sheet.append(header)
|
||||
pattern_fill_style = PatternFillModel(start_color='D9D9D9', end_color='D9D9D9', fill_type='solid')
|
||||
font_style = FontModel(bold=True)
|
||||
self.__set_row_style(1, len(header), pattern_fill_style=pattern_fill_style, font_style=font_style)
|
||||
for index, data in enumerate(rows):
|
||||
format_columns = {
|
||||
"date_columns": []
|
||||
}
|
||||
for i in range(0, len(data)):
|
||||
if isinstance(data[i], datetime.datetime):
|
||||
data[i] = data[i].strftime("%Y-%m-%d %H:%M:%S")
|
||||
format_columns["date_columns"].append(i + 1)
|
||||
elif isinstance(data[i], bool):
|
||||
data[i] = 1 if data[i] else 0
|
||||
self.sheet.append(data)
|
||||
self.__set_row_style(index + 2 if header else index + 1, len(data))
|
||||
self.__set_row_format(index + 2 if header else index + 1, format_columns)
|
||||
self.__auto_width()
|
||||
self.__set_row_border()
|
||||
|
||||
def save_excel(self, path: str = "excel_manage"):
|
||||
"""
|
||||
保存 excel 文件到本地 static 目录
|
||||
:param path: static 指定目录类别
|
||||
:return:
|
||||
"""
|
||||
file_path = FileBase.generate_static_file_path(path=path, suffix="xlsx")
|
||||
Path(file_path).parent.mkdir(parents=True, exist_ok=True)
|
||||
self.wb.save(file_path)
|
||||
return {
|
||||
"local_path": file_path,
|
||||
"remote_path": file_path.replace(STATIC_ROOT, STATIC_URL)
|
||||
}
|
||||
|
||||
def __set_row_style(
|
||||
self,
|
||||
row: int,
|
||||
max_column: int,
|
||||
alignment_style: AlignmentModel = AlignmentModel(),
|
||||
font_style: FontModel = FontModel(),
|
||||
pattern_fill_style: PatternFillModel = PatternFillModel()
|
||||
):
|
||||
"""
|
||||
设置行样式
|
||||
:param row: 行
|
||||
:param max_column: 最大列
|
||||
:param alignment_style: 单元格内容的对齐设置
|
||||
:param font_style: 单元格内容的字体样式设置
|
||||
:param pattern_fill_style: 单元格的填充模式设置
|
||||
:return:
|
||||
"""
|
||||
for index in range(0, max_column):
|
||||
alignment = Alignment(**alignment_style.model_dump())
|
||||
font = Font(**font_style.model_dump())
|
||||
pattern_fill = PatternFill(**pattern_fill_style.model_dump())
|
||||
self.sheet.cell(row=row, column=index + 1).alignment = alignment
|
||||
self.sheet.cell(row=row, column=index + 1).font = font
|
||||
self.sheet.cell(row=row, column=index + 1).fill = pattern_fill
|
||||
|
||||
def __set_row_format(self, row: int, columns: dict):
|
||||
"""
|
||||
格式化行数据类型
|
||||
:param row: 行
|
||||
:param columns: 列数据
|
||||
:return:
|
||||
"""
|
||||
for index in columns.get("date_columns", []):
|
||||
self.sheet.cell(row=row, column=index).number_format = "yyyy-mm-dd h:mm:ss"
|
||||
|
||||
def __set_row_border(self):
|
||||
"""
|
||||
设置行边框
|
||||
:return:
|
||||
"""
|
||||
# 创建 Border 对象并设置边框样式
|
||||
border = Border(
|
||||
left=Side(border_style="thin", color="000000"),
|
||||
right=Side(border_style="thin", color="000000"),
|
||||
top=Side(border_style="thin", color="000000"),
|
||||
bottom=Side(border_style="thin", color="000000")
|
||||
)
|
||||
# 设置整个表格的边框
|
||||
for row in self.sheet.iter_rows():
|
||||
for cell in row:
|
||||
cell.border = border
|
||||
|
||||
def __auto_width(self):
|
||||
"""
|
||||
设置自适应列宽
|
||||
:return:
|
||||
"""
|
||||
# 设置一个字典用于保存列宽数据
|
||||
dims = {}
|
||||
# 遍历表格数据,获取自适应列宽数据
|
||||
for row in self.sheet.rows:
|
||||
for cell in row:
|
||||
if cell.value:
|
||||
# 遍历整个表格,把该列所有的单元格文本进行长度对比,找出最长的单元格
|
||||
# 在对比单元格文本时需要将中文字符识别为1.7个长度,英文字符识别为1个,这里只需要将文本长度直接加上中文字符数量即可
|
||||
# re.findall('([\u4e00-\u9fa5])', cell.value)能够识别大部分中文字符
|
||||
cell_len = 0.7 * len(re.findall('([\u4e00-\u9fa5])', str(cell.value))) + len(str(cell.value))
|
||||
dims[cell.column] = max((dims.get(cell.column, 0), cell_len))
|
||||
for col, value in dims.items():
|
||||
# 设置列宽,get_column_letter用于获取数字列号对应的字母列号,最后值+2是用来调整最终效果的
|
||||
self.sheet.column_dimensions[get_column_letter(col)].width = value + 10
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
关闭文件
|
||||
:return:
|
||||
"""
|
||||
self.wb.close()
|
65
utils/excel/excel_schema.py
Normal file
65
utils/excel/excel_schema.py
Normal file
@ -0,0 +1,65 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2023/08/24 22:19
|
||||
# @File : excel_schema.py
|
||||
# @IDE : PyCharm
|
||||
# @desc :
|
||||
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class AlignmentModel(BaseModel):
|
||||
horizontal: str = Field('center', description="水平对齐方式。可选值:'left'、'center'、'right'、'justify'、'distributed'")
|
||||
vertical: str = Field('center', description="垂直对齐方式。可选值:'top'、'center'、'bottom'、'justify'、'distributed'")
|
||||
textRotation: int = Field(0, description="文本旋转角度(以度为单位)。默认为 0。")
|
||||
wrapText: bool = Field(None, description="自动换行文本。设置为 True 时,如果文本内容超出单元格宽度,会自动换行显示。")
|
||||
shrinkToFit: bool = Field(
|
||||
None,
|
||||
description="缩小字体以适应单元格。设置为 True 时,如果文本内容超出单元格宽度,会自动缩小字体大小以适应。"
|
||||
)
|
||||
indent: int = Field(0, description="文本缩进级别。默认为 0。")
|
||||
relativeIndent: int = Field(0, description="相对缩进级别。默认为 0。")
|
||||
justifyLastLine: bool = Field(
|
||||
None,
|
||||
description="对齐换行文本的末尾行。设置为 True 时,如果设置了文本换行,末尾的行也会被对齐。"
|
||||
)
|
||||
readingOrder: int = Field(0, description="阅读顺序。默认为 0。")
|
||||
|
||||
class Config:
|
||||
title = "对齐设置模型"
|
||||
description = "用于设置单元格内容的对齐样式。"
|
||||
|
||||
|
||||
class FontModel(BaseModel):
|
||||
name: str = Field(None, description="字体名称")
|
||||
size: float = Field(None, description="字体大小(磅为单位)")
|
||||
bold: bool = Field(None, description="是否加粗")
|
||||
italic: bool = Field(None, description="是否斜体")
|
||||
underline: str = Field(None, description="下划线样式。可选值:'single'、'double'、'singleAccounting'、'doubleAccounting'")
|
||||
strikethrough: bool = Field(None, description="是否有删除线")
|
||||
outline: bool = Field(None, description="是否轮廓字体")
|
||||
shadow: bool = Field(None, description="是否阴影字体")
|
||||
condense: bool = Field(None, description="是否压缩字体")
|
||||
extend: bool = Field(None, description="是否扩展字体")
|
||||
vertAlign: str = Field(None, description="垂直对齐方式。可选值:'superscript'、'subscript'、'baseline'")
|
||||
color: dict = Field(None, description="字体颜色")
|
||||
scheme: str = Field(None, description="字体方案。可选值:'major'、'minor'")
|
||||
charset: int = Field(None, description="字符集编号")
|
||||
family: int = Field(None, description="字体族编号")
|
||||
|
||||
class Config:
|
||||
title = "字体设置模型"
|
||||
description = "用于设置单元格内容的字体样式"
|
||||
|
||||
|
||||
class PatternFillModel(BaseModel):
|
||||
start_color: str = Field("FFFFFF", description="起始颜色(RGB 值或颜色名称)")
|
||||
end_color: str = Field("FFFFFF", description="结束颜色(RGB 值或颜色名称)")
|
||||
fill_type: str = Field("solid", description="填充类型('none'、'solid'、'darkDown' 等)")
|
||||
|
||||
class Config:
|
||||
title = "填充模式设置模型"
|
||||
description = "单元格的填充模式设置"
|
||||
|
165
utils/excel/import_manage.py
Normal file
165
utils/excel/import_manage.py
Normal file
@ -0,0 +1,165 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2021/12/5 8:45
|
||||
# @File : import_manage.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 数据导入管理
|
||||
|
||||
from typing import List
|
||||
from fastapi import UploadFile
|
||||
from core.exception import CustomException
|
||||
from utils import status
|
||||
from .excel_manage import ExcelManage
|
||||
from utils.file.file_manage import FileManage
|
||||
from .write_xlsx import WriteXlsx
|
||||
from ..tools import list_dict_find
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class FieldType(Enum):
|
||||
list = "list"
|
||||
str = "str"
|
||||
|
||||
|
||||
class ImportManage(ExcelManage):
|
||||
"""
|
||||
数据导入管理
|
||||
|
||||
只支持 XLSX 类型文件:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
|
||||
|
||||
1. 判断文件类型
|
||||
2. 保存文件为临时文件
|
||||
3. 获取文件中的数据
|
||||
4. 逐行检查数据,通过则创建数据
|
||||
5. 不通过则添加到错误列表
|
||||
6. 统计数量并返回
|
||||
"""
|
||||
|
||||
file_type = ["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"]
|
||||
|
||||
def __init__(self, file: UploadFile, headers: List[dict]):
|
||||
super().__init__()
|
||||
self.__table_data = None
|
||||
self.__table_header = None
|
||||
self.errors = []
|
||||
self.success = []
|
||||
self.success_number = 0
|
||||
self.error_number = 0
|
||||
self.check_file_type(file)
|
||||
self.file = file
|
||||
self.headers = headers
|
||||
|
||||
@classmethod
|
||||
def check_file_type(cls, file: UploadFile) -> None:
|
||||
"""
|
||||
验证文件类型
|
||||
:param file: 上传文件
|
||||
:return:
|
||||
"""
|
||||
if file.content_type not in cls.file_type:
|
||||
raise CustomException(msg="文件类型必须为xlsx类型", code=status.HTTP_ERROR)
|
||||
|
||||
async def get_table_data(
|
||||
self,
|
||||
file_path: str = None,
|
||||
sheet_name: str = None,
|
||||
header_row: int = 1,
|
||||
data_row: int = 2
|
||||
) -> None:
|
||||
"""
|
||||
获取表格数据与表头
|
||||
:param file_path:
|
||||
:param sheet_name:
|
||||
:param header_row: 表头在第几行
|
||||
:param data_row: 数据开始行
|
||||
:return:
|
||||
"""
|
||||
if file_path:
|
||||
__filename = file_path
|
||||
else:
|
||||
__filename = await FileManage.async_save_temp_file(self.file)
|
||||
self.open_sheet(sheet_name=sheet_name, file=__filename, read_only=True)
|
||||
self.__table_header = self.get_header(header_row, len(self.headers), asterisk=True)
|
||||
self.__table_data = self.readlines(min_row=data_row, max_col=len(self.headers))
|
||||
self.close()
|
||||
|
||||
def check_table_data(self) -> None:
|
||||
"""
|
||||
检查表格数据
|
||||
:return:
|
||||
"""
|
||||
for row in self.__table_data:
|
||||
result = self.__check_row(row)
|
||||
if not result[0]:
|
||||
row.append(result[1])
|
||||
self.errors.append(row)
|
||||
self.error_number += 1
|
||||
else:
|
||||
self.success_number += 1
|
||||
self.success.append(result[1])
|
||||
|
||||
def __check_row(self, row: list) -> tuple:
|
||||
"""
|
||||
检查行数据
|
||||
|
||||
检查条件:
|
||||
1. 检查是否为必填项
|
||||
2. 检查是否为选项列表
|
||||
3. 检查是否符合规则
|
||||
:param row: 数据行
|
||||
:return:
|
||||
"""
|
||||
data = {}
|
||||
for index, cell in enumerate(row):
|
||||
value = cell
|
||||
field = self.headers[index]
|
||||
label = self.__table_header[index]
|
||||
if not cell and field.get("required", False):
|
||||
return False, f"{label}不能为空!"
|
||||
elif field.get("options", []) and cell:
|
||||
item = list_dict_find(field.get("options", []), "label", cell)
|
||||
if item:
|
||||
value = item.get("value")
|
||||
else:
|
||||
return False, f"请选择正确的{label}"
|
||||
elif field.get("rules", []) and cell:
|
||||
rules = field.get("rules")
|
||||
for validator in rules:
|
||||
try:
|
||||
validator(str(cell))
|
||||
except ValueError as e:
|
||||
return False, f"{label}:{e.__str__()}"
|
||||
if value:
|
||||
field_type = field.get("type", FieldType.str)
|
||||
if field_type == FieldType.list:
|
||||
data[field.get("field")] = [value]
|
||||
elif field_type == FieldType.str:
|
||||
data[field.get("field")] = str(value)
|
||||
else:
|
||||
data[field.get("field")] = value
|
||||
data["old_data_list"] = row
|
||||
return True, data
|
||||
|
||||
def generate_error_url(self) -> str:
|
||||
"""
|
||||
成功错误数据的文件链接
|
||||
:return:
|
||||
"""
|
||||
if self.error_number <= 0:
|
||||
return ""
|
||||
em = WriteXlsx()
|
||||
em.create_excel(sheet_name="用户导入失败数据", save_static=True)
|
||||
em.generate_template(self.headers, max_row=self.error_number)
|
||||
em.write_list(self.errors)
|
||||
em.close()
|
||||
return em.get_file_url()
|
||||
|
||||
def add_error_data(self, row: dict) -> None:
|
||||
"""
|
||||
增加错误数据
|
||||
:param row: 错误的数据行
|
||||
:return:
|
||||
"""
|
||||
self.errors.append(row)
|
||||
self.error_number += 1
|
||||
self.success_number -= 1
|
129
utils/excel/write_xlsx.py
Normal file
129
utils/excel/write_xlsx.py
Normal file
@ -0,0 +1,129 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2022/11/11 12:01
|
||||
# @File : write_xlsx.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 简要说明
|
||||
|
||||
"""
|
||||
XlsxWriter:https://github.com/jmcnamara/XlsxWriter
|
||||
博客教程:https://blog.csdn.net/lemonbit/article/details/113855768
|
||||
"""
|
||||
|
||||
import os.path
|
||||
import xlsxwriter
|
||||
from typing import List
|
||||
from application.settings import STATIC_ROOT, STATIC_URL
|
||||
from utils.file.file_base import FileBase
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class WriteXlsx:
|
||||
"""
|
||||
写入xlsx文件
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.file_path = None
|
||||
self.sheet_name = None
|
||||
self.wb = None
|
||||
self.sheet = None
|
||||
|
||||
def create_excel(self, file_path: str = None, sheet_name: str = "sheet1", save_static: bool = False) -> None:
|
||||
"""
|
||||
创建 excel 文件
|
||||
:param file_path: 文件绝对路径或相对路径
|
||||
:param sheet_name: sheet 名称
|
||||
:param save_static: 保存方式 static 静态资源或者临时文件
|
||||
:return:
|
||||
"""
|
||||
if not file_path:
|
||||
if save_static:
|
||||
self.file_path = FileBase.generate_static_file_path(path="write_xlsx", suffix="xlsx")
|
||||
else:
|
||||
self.file_path = FileBase.generate_temp_file_path(suffix="xlsx")
|
||||
elif not os.path.isabs(file_path):
|
||||
if save_static:
|
||||
self.file_path = FileBase.generate_static_file_path(path="write_xlsx", filename=file_path)
|
||||
else:
|
||||
self.file_path = FileBase.generate_temp_file_path(filename=file_path)
|
||||
else:
|
||||
self.file_path = file_path
|
||||
Path(self.file_path).parent.mkdir(parents=True, exist_ok=True)
|
||||
self.sheet_name = sheet_name
|
||||
self.wb = xlsxwriter.Workbook(self.file_path)
|
||||
self.sheet = self.wb.add_worksheet(sheet_name)
|
||||
|
||||
def generate_template(self, headers: List[dict] = None, max_row: int = 101) -> None:
|
||||
"""
|
||||
生成模板
|
||||
:param headers: 表头
|
||||
:param max_row: 设置下拉列表至最大行
|
||||
:return: 文件链接地址
|
||||
"""
|
||||
max_row = max_row + 100
|
||||
for index, field in enumerate(headers):
|
||||
font_format = {
|
||||
'bold': False, # 字体加粗
|
||||
'align': 'center', # 水平位置设置:居中
|
||||
'valign': 'vcenter', # 垂直位置设置,居中
|
||||
'font_size': 11, # '字体大小设置'
|
||||
}
|
||||
if field.get("required", False):
|
||||
# 设置单元格必填样式
|
||||
field["label"] = "* " + field["label"]
|
||||
font_format["font_color"] = "red"
|
||||
if field.get("options", False):
|
||||
# 添加数据验证,下拉列表
|
||||
validate = {'validate': 'list', 'source': [item.get("label") for item in field.get("options", [])]}
|
||||
self.sheet.data_validation(1, index, max_row, index, validate)
|
||||
cell_format = self.wb.add_format(font_format)
|
||||
self.sheet.write(0, index, field.get("label"), cell_format)
|
||||
# 设置列宽
|
||||
self.sheet.set_column(0, len(headers) - 1, 22)
|
||||
# 设置行高
|
||||
self.sheet.set_row(0, 25)
|
||||
|
||||
def write_list(self, rows: list, start_row: int = 1) -> None:
|
||||
"""
|
||||
写入 excel文件
|
||||
|
||||
:param rows: 行数据集
|
||||
:param start_row: 开始行
|
||||
"""
|
||||
font_format = {
|
||||
'bold': False, # 字体加粗
|
||||
'align': 'center', # 水平位置设置:居中
|
||||
'valign': 'vcenter', # 垂直位置设置,居中
|
||||
'font_size': 11, # '字体大小设置'
|
||||
}
|
||||
cell_format = self.wb.add_format(font_format)
|
||||
for index, row in enumerate(rows):
|
||||
row_number = index+start_row
|
||||
self.sheet.write_row(row_number, 0, row, cell_format)
|
||||
# 设置列宽
|
||||
self.sheet.set_column(0, len(rows[0]) - 1, 22)
|
||||
# 设置行高
|
||||
self.sheet.set_default_row(25)
|
||||
|
||||
def close(self) -> None:
|
||||
"""
|
||||
关闭文件
|
||||
"""
|
||||
self.wb.close()
|
||||
|
||||
def get_file_url(self) -> str:
|
||||
"""
|
||||
获取访问文件的 url
|
||||
:return:
|
||||
"""
|
||||
if not self.file_path:
|
||||
raise ValueError("还未创建文件,请先创建 excel 文件!")
|
||||
assert isinstance(self.file_path, str)
|
||||
if self.file_path.startswith(STATIC_ROOT):
|
||||
return self.file_path.replace(STATIC_ROOT, STATIC_URL)
|
||||
else:
|
||||
print("write_xlsx 生成文件:", self.file_path)
|
||||
raise ValueError("生成文件为临时文件,无法访问!")
|
||||
|
Reference in New Issue
Block a user