style, utils 수정
This commit is contained in:
@@ -679,6 +679,12 @@ export const SearchItem = styled.div`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const DownloadContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
`;
|
||||||
|
|
||||||
export const SearchRow = styled.div`
|
export const SearchRow = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@@ -689,4 +695,43 @@ export const SearchRow = styled.div`
|
|||||||
padding-top: 15px;
|
padding-top: 15px;
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
}
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const TabScroll = styled.div`
|
||||||
|
width: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const TabItem = styled(Link)`
|
||||||
|
display: inline-flex;
|
||||||
|
width: 120px;
|
||||||
|
height: 30px;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
background: #f9f9f9;
|
||||||
|
border-left: 1px solid #d9d9d9;
|
||||||
|
&:hover {
|
||||||
|
background: #888;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
${props =>
|
||||||
|
props.$state === 'active' &&
|
||||||
|
`
|
||||||
|
background: #888;
|
||||||
|
color: #fff;`}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const TabWrapper = styled.ul`
|
||||||
|
display: flex;
|
||||||
|
li:first-child {
|
||||||
|
${TabItem} {
|
||||||
|
border-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const CircularProgressWrapper = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
`;
|
`;
|
||||||
@@ -86,4 +86,109 @@ export const getFieldLabel = (key, value) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return key;
|
return key;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const numberFormatter = {
|
||||||
|
formatCurrency: (number, decimals = 2) => {
|
||||||
|
if (number === null || number === undefined) return '0';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const num = typeof number === 'string' ? parseFloat(number) : number;
|
||||||
|
if (isNaN(num)) return '0';
|
||||||
|
|
||||||
|
// 정수인지 확인
|
||||||
|
const isInteger = Number.isInteger(num);
|
||||||
|
|
||||||
|
// 작은 수이거나 소수점이 있는 경우
|
||||||
|
if ((Math.abs(num) < 1 && num !== 0) || !isInteger) {
|
||||||
|
return num.toFixed(decimals);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 정수인 경우
|
||||||
|
return new Intl.NumberFormat('ko-KR', {
|
||||||
|
minimumFractionDigits: 0,
|
||||||
|
maximumFractionDigits: 0
|
||||||
|
}).format(num);
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Currency formatting error:', e);
|
||||||
|
return '0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API 응답으로부터 파일 다운로드를 처리하는 함수
|
||||||
|
* @param {Object} response - API 응답 객체
|
||||||
|
* @param {Object} response.data - 응답 데이터
|
||||||
|
* @param {Object} response.headers - 응답 헤더
|
||||||
|
* @param {Object} options - 추가 옵션
|
||||||
|
* @param {string} options.defaultFileName - 기본 파일명 (기본값: 'download')
|
||||||
|
* @returns {void}
|
||||||
|
* @throws {Error} 데이터가 없거나 파일 형식이 잘못된 경우
|
||||||
|
*/
|
||||||
|
export const responseFileDownload = (response, options = {}) => {
|
||||||
|
const { defaultFileName = 'download' } = options;
|
||||||
|
|
||||||
|
if (!response.data) {
|
||||||
|
console.log(response);
|
||||||
|
throw new Error('No data received');
|
||||||
|
}
|
||||||
|
|
||||||
|
const contentType = response.headers['content-type'] || response.headers['Content-Type'];
|
||||||
|
const contentDisposition = response.headers['content-disposition'] || response.headers['Content-Disposition'];
|
||||||
|
|
||||||
|
// Excel이나 ZIP 파일 형식 검증
|
||||||
|
if (!contentType.includes('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') &&
|
||||||
|
!contentType.includes('application/zip')) {
|
||||||
|
console.log(response);
|
||||||
|
throw new Error(`잘못된 파일 형식입니다. Content-Type: ${contentType}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
let fileName = defaultFileName;
|
||||||
|
let fileExtension = '.xlsx';
|
||||||
|
let mimeType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
|
||||||
|
|
||||||
|
// ZIP 파일 처리
|
||||||
|
if (contentType && contentType.includes('application/zip')) {
|
||||||
|
fileExtension = '.zip';
|
||||||
|
mimeType = 'application/zip';
|
||||||
|
fileName = `${defaultFileName}_multiple_files`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Content-Disposition에서 파일명 추출
|
||||||
|
if (contentDisposition) {
|
||||||
|
const fileNameMatch = contentDisposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/);
|
||||||
|
if (fileNameMatch && fileNameMatch[1]) {
|
||||||
|
fileName = decodeURIComponent(fileNameMatch[1].replace(/['"]/g, ''));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fileName = fileName + fileExtension;
|
||||||
|
}
|
||||||
|
|
||||||
|
const blob = new Blob([response.data], { type: mimeType });
|
||||||
|
|
||||||
|
// 파일 유효성 검사
|
||||||
|
if (blob.size === 0) {
|
||||||
|
throw new Error('다운로드된 파일이 비어있습니다.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blob.size < 1024) {
|
||||||
|
throw new Error('파일 크기가 너무 작습니다. 올바른 Excel 파일이 아닐 수 있습니다.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 파일 다운로드 실행
|
||||||
|
const href = URL.createObjectURL(blob);
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = href;
|
||||||
|
link.download = fileName;
|
||||||
|
link.style.display = 'none';
|
||||||
|
link.rel = 'noopener noreferrer';
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
|
||||||
|
// 정리
|
||||||
|
link.remove();
|
||||||
|
window.URL.revokeObjectURL(href);
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user