Files
ev-charger-as/frontend/static/js/api.js
byun 9f0f4326fe 기능 개선 — 사진 업로드, HEIC 지원, 재조치 흐름, 신고 순번, 모바일 UI
- 이미지 압축: 삼성/네이버 브라우저 호환, URL.createObjectURL 방식으로 메모리 절감,
  대용량 PNG/HEIC 처리, blob 유효성 검증, 순차 압축으로 모바일 OOM 방지
- HEIC/HEIF 지원: pillow-heif 서버사이드 변환, Pillow 12.2.0 업그레이드
- 조치 페이지: '조치 완료 저장' 단일 버튼으로 단순화
- 재조치 흐름: 관리자 재조치 요청 시 이전 조치 이력을 번호 카드로 순차 표시
- 신고 순번: 전체 기준 ROW_NUMBER(oldest=1) 순번 표시, 삭제 gap 제거
- 모바일 탭바: position:fixed 적용으로 nav 하단 흰 여백 제거

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 05:38:33 +09:00

83 lines
2.8 KiB
JavaScript

const API = (() => {
const BASE = '/api';
function token() { return localStorage.getItem('ev_token') || ''; }
async function req(method, path, body = null, isForm = false) {
const headers = {};
if (token()) headers['Authorization'] = 'Bearer ' + token();
let fetchBody = null;
if (body) {
if (isForm) {
fetchBody = body;
} else {
headers['Content-Type'] = 'application/json';
fetchBody = JSON.stringify(body);
}
}
const res = await fetch(BASE + path, { method, headers, body: fetchBody });
if (res.status === 401) {
if (location.pathname !== '/pages/login.html') {
sessionStorage.setItem('ev_redirect', location.pathname + location.search);
}
Auth.logout(); return;
}
if (!res.ok) {
const err = await res.json().catch(() => ({ detail: '오류가 발생했습니다.' }));
throw new Error(err.detail || '오류');
}
const ct = res.headers.get('content-type') || '';
if (ct.includes('spreadsheet') || ct.includes('octet') || ct.includes('excel')) {
return res.blob();
}
return res.json().catch(() => ({}));
}
// 엑셀 다운로드 전용 함수 — 인증 토큰 포함, 에러 처리 강화
async function download(path, filename) {
try {
const headers = {};
if (token()) headers['Authorization'] = 'Bearer ' + token();
const res = await fetch(BASE + path, { method: 'GET', headers });
if (res.status === 401) {
if (location.pathname !== '/pages/login.html') {
sessionStorage.setItem('ev_redirect', location.pathname + location.search);
}
Auth.logout(); return;
}
if (!res.ok) {
const err = await res.json().catch(() => ({ detail: '다운로드 오류' }));
throw new Error(err.detail || '다운로드 오류');
}
const blob = await res.blob();
if (!blob || blob.size === 0) {
alert('데이터가 없어 엑셀 파일을 생성할 수 없습니다.');
return;
}
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
} catch(e) {
alert('엑셀 다운로드 실패: ' + e.message);
}
}
return {
get: (path) => req('GET', path),
post: (path, body) => req('POST', path, body, body instanceof FormData),
put: (path, body) => req('PUT', path, body, body instanceof FormData),
patch: (path, body) => req('PATCH', path, body, body instanceof FormData),
delete: (path, body) => req('DELETE', path, body !== undefined ? body : null),
download,
};
})();