반응형
const handleExcelDownLoad = async () => {
try {
const response = await fetch(
`${baseURL}/api/admin/work/export_excel?company_id=${companyId}`,
{
method: "GET",
credentials: "include",
},
);
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
const now = new Date();
const pad = (n: number) => String(n).padStart(2, "0");
// 날짜 + 시간 → "20250808_153012" 형태로 포맷
const timestamp = `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(
now.getDate(),
)}_${pad(now.getHours())}${pad(now.getMinutes())}${pad(
now.getSeconds(),
)}`;
// 파일명 구성
const fileName = `${companyName}_${timestamp}.xlsx`;
// 적용
a.download = fileName;
document.body.appendChild(a);
a.click();
a.remove();
window.URL.revokeObjectURL(url);
} catch (error) {
console.error("Excel 다운로드 실패:", error);
}
};
async def export_excel(company_id: int, db):
try:
original_version = aliased(data.content_version)
latest_version = aliased(data.content_version)
# 글 생성 초안 가져오기
version1_rows = (
db.query(
data.content_version.content_id,
data.content_version.field_name,
data.content_version.content_text,
)
.filter(data.content_version.version == 1)
.all()
)
original_text_map = defaultdict(list)
for row in version1_rows:
field = f"[{row.field_name}]" if row.field_name else ""
text = f"{field}\n{row.content_text}"
original_text_map[row.content_id].append(text)
# 문자열 결합
for content_id in original_text_map:
original_text_map[content_id] = "\n\n".join(original_text_map[content_id])
# 최종본 가져오기
latest_subq = (
db.query(
latest_version.content_id.label("content_id"),
latest_version.field_name,
latest_version.content_text.label("final_text"),
)
.filter(latest_version.version == 7)
.subquery()
)
results = (
db.query(
data.content.id,
data.content.main_content,
data.content.sub_content,
data.content.title,
data.content.status,
data.user.user_name,
data.user.user_email,
data.department.department_name,
data.content.last_modified_at,
data.content.created_at,
latest_subq.c.final_text,
)
.join(data.content_type, data.content.content_type_id == data.content_type.id)
.join(data.company, data.content_type.company_id == data.company.id)
.join(data.user, data.content.user_id == data.user.id)
.join(data.department, data.user.department_id == data.department.id)
.outerjoin(latest_subq, latest_subq.c.content_id == data.content.id)
.filter(data.company.id == company_id)
.all()
)
status_map = {"temp": "임시저장", "progress": "작업중", "done":"작업완료"}
export_rows = []
for row in results:
export_rows.append({
"대분류": row.main_content or "",
"소분류": row.sub_content or "",
"제목": row.title or "제목없음",
"생산자": row.user_name,
"소속부서": row.department_name,
"마지막 수정일": row.last_modified_at.strftime("%Y-%m-%d %H:%M") if row.last_modified_at else "",
"상태": status_map[row.status.value],
"초안": original_text_map.get(row.id, ""),
"생성된 글": row.final_text or "생성된 글이 존재하지 않습니다.",
})
if not export_rows:
# 데이터가 없는 경우 빈 엑셀 파일 생성
df = pd.DataFrame(columns=[
"대분류", "소분류", "제목", "생산자",
"소속부서", "마지막 수정일", "상태",
"초안", "생성된 글",
])
else:
df = pd.DataFrame(export_rows)
# BytesIO 객체 생성
output = io.BytesIO()
# Excel 파일 생성
with pd.ExcelWriter(output, engine='openpyxl') as writer:
df.to_excel(writer, index=False, sheet_name="Exported Contents")
# 워크시트 가져오기
worksheet = writer.sheets["Exported Contents"]
from openpyxl.styles import Alignment
from openpyxl.utils import get_column_letter
center_columns = ["대분류", "소분류", "제목", "생산자",
"소속부서", "마지막 수정일", "상태"]
column_name_to_index = {name: idx for idx, name in enumerate(df.columns, start=1)}
for col_name in center_columns:
col_idx = column_name_to_index.get(col_name)
if not col_idx:
continue
col_letter = get_column_letter(col_idx)
for row in range(2, worksheet.max_row + 1):
worksheet[f"{col_letter}{row}"].alignment = Alignment(horizontal="center")
# 열 너비 자동 조정
for column in worksheet.columns:
max_length = 0
column_letter = column[0].column_letter
for cell in column:
try:
if len(str(cell.value)) > max_length:
max_length = len(str(cell.value))
except:
pass
adjusted_width = min(max_length + 10, 50) # 최대 50으로 제한
worksheet.column_dimensions[column_letter].width = adjusted_width
# 포인터를 처음으로 이동
output.seek(0)
# 파일명에 타임스탬프 추가
from datetime import datetime
timestamp = datetime.now().strftime("%Y%m%d")
filename = f"contents_{timestamp}.xlsx"
return StreamingResponse(
output,
media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
headers={
"Content-Disposition": f"attachment; filename={filename}",
"Content-Type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
},
)
except Exception as e:
import traceback
traceback.print_exc()
return JSONResponse(
status_code=500,
content={"error": f"Excel export failed: {str(e)}"}
)반응형
'기능 > 기타' 카테고리의 다른 글
| [RTE] React Tiptap 에디터 적용하기 2 (7) | 2026.01.18 |
|---|---|
| [RTE] React Tiptap 에디터 적용하기 (0) | 2026.01.07 |
| [SMTP] 구글 이메일 발송 (3) | 2025.08.08 |
