비즈니스 로그조회 추가

This commit is contained in:
2025-03-19 10:56:57 +09:00
parent f2d7c87f38
commit a0087a1e29
15 changed files with 1464 additions and 54 deletions

View File

@@ -7,7 +7,7 @@ import {
import { ViewTitleCountType } from '../../../assets/data';
import { TitleItem, TitleItemLabel, TitleItemValue } from '../../../styles/ModuleComponents';
const ViewTableInfo = ({children, total, total_all, handleOrderBy, handlePageSize, countType = ViewTitleCountType.total}) => {
const ViewTableInfo = ({children, total, total_all, orderType, handleOrderBy, pageType, handlePageSize, countType = ViewTitleCountType.total}) => {
return (
<TableInfo>
{total !== undefined && total_all !== undefined &&
@@ -27,18 +27,44 @@ const ViewTableInfo = ({children, total, total_all, handleOrderBy, handlePageSiz
}
</ListCount>}
<ListOption>
<SelectInput className="input-select" onChange={e => handleOrderBy(e.target.value)}>
<option value="DESC">내림차순</option>
<option value="ASC">오름차순</option>
</SelectInput>
<SelectInput name="" id="" className="input-select" onChange={e => handlePageSize(e.target.value)}>
<option value="50">50</option>
<option value="100">100</option>
</SelectInput>
<OrderBySelect orderType={orderType} handleOrderBy={handleOrderBy} />
<PageSelect pageType={pageType} handlePageSize={handlePageSize} />
{children}
</ListOption>
</TableInfo>
);
};
const OrderBySelect = ({orderType, handleOrderBy}) => {
return(
orderType === "asc" ?
<SelectInput className="input-select" onChange={e => handleOrderBy(e.target.value)}>
<option value="ASC">오름차순</option>
<option value="DESC">내림차순</option>
</SelectInput>
:
<SelectInput className="input-select" onChange={e => handleOrderBy(e.target.value)}>
<option value="DESC">내림차순</option>
<option value="ASC">오름차순</option>
</SelectInput>
);
}
const PageSelect = ({pageType, handlePageSize}) => {
return(
pageType === "B" ?
<SelectInput name="" id="" className="input-select" onChange={e => handlePageSize(e.target.value)}>
<option value="500">500</option>
<option value="1000">1000</option>
<option value="5000">5000</option>
<option value="10000">10000</option>
</SelectInput>
:
<SelectInput name="" id="" className="input-select" onChange={e => handlePageSize(e.target.value)}>
<option value="50">50</option>
<option value="100">100</option>
</SelectInput>
);
}
export default ViewTableInfo;

View File

@@ -1,25 +1,99 @@
import * as XLSX from 'xlsx-js-style';
import { ExcelDownButton } from '../../../styles/ModuleComponents';
const ExcelDownloadButton = ({ tableRef, fileName = 'download.xlsx', sheetName = 'Sheet1' }) => {
const ExcelDownloadButton = ({ tableRef, data, fileName = 'download.xlsx', sheetName = 'Sheet1' }) => {
const isNumeric = (value) => {
// 숫자 또는 숫자 문자열인지 확인
return !isNaN(value) && !isNaN(parseFloat(value));
};
const downloadExcel = () => {
// 테두리 스타일 정의
const borderStyle = {
style: "thin",
color: { rgb: "000000" }
};
// 기본 셀 스타일
const baseCellStyle = {
font: {
name: "맑은 고딕",
sz: 11
},
border: {
top: borderStyle,
bottom: borderStyle,
left: borderStyle,
right: borderStyle
}
};
// 헤더 스타일
const headerStyle = {
...baseCellStyle,
font: {
...baseCellStyle.font,
bold: true
},
alignment: {
horizontal: 'center',
vertical: 'center'
},
fill: {
fgColor: { rgb: "d9e1f2" },
patternType: "solid"
}
};
// 기본 데이터 셀 스타일
const dataStyle = {
...baseCellStyle,
alignment: {
horizontal: 'left',
vertical: 'center',
wrapText: true
}
};
const flattenObject = (obj, prefix = '') => {
return Object.keys(obj).reduce((acc, key) => {
const prefixedKey = prefix ? `${prefix}.${key}` : key;
if (typeof obj[key] === 'object' && obj[key] !== null && !Array.isArray(obj[key])) {
Object.assign(acc, flattenObject(obj[key], prefixedKey));
} else if (Array.isArray(obj[key])) {
// 배열은 JSON 문자열로 변환
acc[prefixedKey] = JSON.stringify(obj[key]);
} else {
acc[prefixedKey] = obj[key];
}
return acc;
}, {});
};
const downloadTableExcel = () => {
try {
if (!tableRef.current) return;
if (!tableRef || !tableRef.current) {
alert('테이블 참조가 없습니다.');
return;
}
const tableElement = tableRef.current;
const headerRows = tableElement.getElementsByTagName('thead')[0].getElementsByTagName('tr');
const bodyRows = tableElement.getElementsByTagName('tbody')[0].getElementsByTagName('tr');
// 일반 행만 포함 (상세 행 제외)
const normalBodyRows = Array.from(bodyRows).filter(row => {
// 상세 행은 colspan 속성이 있는 td를 포함
const hasTdWithColspan = Array.from(row.cells).some(cell => cell.hasAttribute('colspan'));
return !hasTdWithColspan;
});
// 헤더 데이터 추출
const headers = Array.from(headerRows[0].cells).map(cell => cell.textContent);
// 바디 데이터 추출 및 숫자 타입 처리
const bodyData = Array.from(bodyRows).map(row =>
const bodyData = normalBodyRows.map(row =>
Array.from(row.cells).map(cell => {
const value = cell.textContent;
return isNumeric(value) ? parseFloat(value) : value;
@@ -29,41 +103,6 @@ const ExcelDownloadButton = ({ tableRef, fileName = 'download.xlsx', sheetName =
// 워크북 생성
const wb = XLSX.utils.book_new();
// 테두리 스타일 정의
const borderStyle = {
style: "thin",
color: { rgb: "000000" }
};
// 스타일 정의
const centerStyle = {
font: {
name: "맑은 고딕",
sz: 11
},
alignment: {
horizontal: 'right',
vertical: 'right'
},
border: {
top: borderStyle,
bottom: borderStyle,
left: borderStyle,
right: borderStyle
}
};
const headerStyle = {
alignment: {
horizontal: 'center',
vertical: 'center'
},
fill: {
fgColor: { rgb: "d9e1f2" },
patternType: "solid"
}
};
// 데이터에 스타일 적용
const wsData = [
// 헤더 행
@@ -75,7 +114,7 @@ const ExcelDownloadButton = ({ tableRef, fileName = 'download.xlsx', sheetName =
...bodyData.map(row =>
row.map(cell => ({
v: cell,
s: centerStyle
s: dataStyle
}))
)
];
@@ -103,8 +142,120 @@ const ExcelDownloadButton = ({ tableRef, fileName = 'download.xlsx', sheetName =
}
};
const downloadDataExcel = () => {
try {
if (!data || !data || data.length === 0) {
alert('다운로드할 데이터가 없습니다.');
return;
}
// 모든 로그 항목을 플랫한 구조로 변환
const flattenedData = data.map(item => {
// 기본 필드
const baseData = {
'logTime': item.logTime,
'GUID': item.userGuid === 'None' ? '' : item.userGuid,
'Nickname': item.userNickname === 'None' ? '' : item.userNickname,
'Account ID': item.accountId === 'None' ? '' : item.accountId,
'Action': item.action,
'Domain' : item.domain === 'None' ? '' : item.domain,
'Tran ID': item.tranId
};
// Actor 데이터 플랫하게 추가
const actorData = item.header && item.header.Actor ?
flattenObject(item.header.Actor, 'Actor') : {};
// Infos 데이터 플랫하게 추가
let infosData = {};
if (item.body && item.body.Infos && Array.isArray(item.body.Infos)) {
item.body.Infos.forEach((info, index) => {
infosData = {
...infosData,
...flattenObject(info, `Info`)
};
});
}
return {
...baseData,
...actorData,
...infosData
};
});
// 모든 항목의 모든 키 수집하여 헤더 생성
const allKeys = new Set();
flattenedData.forEach(item => {
Object.keys(item).forEach(key => allKeys.add(key));
});
const headers = Array.from(allKeys);
// 워크북 생성
const wb = XLSX.utils.book_new();
// 데이터 행들 생성
const dataRows = flattenedData.map(item => {
return headers.map(header => {
const value = item[header] !== undefined ? item[header] : '';
return {
v: value,
s: dataStyle
};
});
});
// 워크시트 데이터 구성
const wsData = [
// 헤더 행
headers.map(h => ({
v: h,
s: headerStyle
})),
// 데이터 행들
...dataRows
];
// 워크시트 생성
const ws = XLSX.utils.aoa_to_sheet(wsData);
// 열 너비 설정 (최소 10, 최대 50)
ws['!cols'] = headers.map((header) => {
// 헤더 길이와 데이터 길이 중 최대값으로 열 너비 결정
const maxLength = Math.max(
header.length * 1.5,
...flattenedData.map(item => {
const value = item[header];
return value !== undefined ? String(value).length * 1.2 : 0;
})
);
return { wch: Math.max(10, Math.min(50, maxLength)) };
});
// 워크시트를 워크북에 추가
XLSX.utils.book_append_sheet(wb, ws, sheetName);
// 엑셀 파일 다운로드
XLSX.writeFile(wb, fileName);
} catch (error) {
console.error('Excel download failed:', error);
alert('엑셀 다운로드 중 오류가 발생했습니다.');
}
};
const handleDownload = () => {
if (tableRef) {
downloadTableExcel();
} else if (data) {
downloadDataExcel();
} else {
alert('유효한 데이터 소스가 없습니다.');
}
};
return (
<ExcelDownButton onClick={downloadExcel}>
<ExcelDownButton onClick={handleDownload}>
엑셀 다운로드
</ExcelDownButton>
);

View File

@@ -0,0 +1,113 @@
import * as XLSX from 'xlsx-js-style';
import { ExcelDownButton } from '../../../styles/ModuleComponents';
const ExcelDownloadButton = ({ tableRef, fileName = 'download.xlsx', sheetName = 'Sheet1' }) => {
const isNumeric = (value) => {
// 숫자 또는 숫자 문자열인지 확인
return !isNaN(value) && !isNaN(parseFloat(value));
};
const downloadExcel = () => {
try {
if (!tableRef.current) return;
const tableElement = tableRef.current;
const headerRows = tableElement.getElementsByTagName('thead')[0].getElementsByTagName('tr');
const bodyRows = tableElement.getElementsByTagName('tbody')[0].getElementsByTagName('tr');
// 헤더 데이터 추출
const headers = Array.from(headerRows[0].cells).map(cell => cell.textContent);
// 바디 데이터 추출 및 숫자 타입 처리
const bodyData = Array.from(bodyRows).map(row =>
Array.from(row.cells).map(cell => {
const value = cell.textContent;
return isNumeric(value) ? parseFloat(value) : value;
})
);
// 워크북 생성
const wb = XLSX.utils.book_new();
// 테두리 스타일 정의
const borderStyle = {
style: "thin",
color: { rgb: "000000" }
};
// 스타일 정의
const centerStyle = {
font: {
name: "맑은 고딕",
sz: 11
},
alignment: {
horizontal: 'right',
vertical: 'right'
},
border: {
top: borderStyle,
bottom: borderStyle,
left: borderStyle,
right: borderStyle
}
};
const headerStyle = {
alignment: {
horizontal: 'center',
vertical: 'center'
},
fill: {
fgColor: { rgb: "d9e1f2" },
patternType: "solid"
}
};
// 데이터에 스타일 적용
const wsData = [
// 헤더 행
headers.map(h => ({
v: h,
s: headerStyle
})),
// 데이터 행들
...bodyData.map(row =>
row.map(cell => ({
v: cell,
s: centerStyle
}))
)
];
// 워크시트 생성
const ws = XLSX.utils.aoa_to_sheet(wsData);
// 열 너비 설정 (최소 8, 최대 50)
ws['!cols'] = headers.map((_, index) => {
const maxLength = Math.max(
headers[index].length * 2,
...bodyData.map(row => String(row[index] || '').length * 1.2)
);
return { wch: Math.max(8, Math.min(50, maxLength)) };
});
// 워크시트를 워크북에 추가
XLSX.utils.book_append_sheet(wb, ws, sheetName);
// 엑셀 파일 다운로드
XLSX.writeFile(wb, fileName);
} catch (error) {
console.error('Excel download failed:', error);
alert('엑셀 다운로드 중 오류가 발생했습니다.');
}
};
return (
<ExcelDownButton onClick={downloadExcel}>
엑셀 다운로드
</ExcelDownButton>
);
};
export default ExcelDownloadButton;