반응형
다양한 방법이 있겠지만
xml을 이용한 excel export 방법
장점? : 화면에 보이는 거의 그대로.. 나오는 느낌 (tag만) (양식 제외) (chart 등 제외)
아래 처럼 하면 document로 찾은 element를 excel로 변환하여 다운로드 가능.
<img/> tag는 되나 chartjs2로 만든 chart는 다운로드 안되었었음.
chartjs2로 만든 그래프를 그대로 export하고 싶었으나 안되서 html2canvas 로 이미지로 만들어서 내보내는걸 테스트중
import * as React from 'react';
import { Line } from 'react-chartjs-2';
import html2canvas from 'html2canvas';
const Test = () => {
const chartData = {
labels: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'],
datasets: [{
data: Array(10).fill(0).map((value, index) => (index)), //data input ex) [1,2,3,4,...]
label: 'There',
borderWidth: 1,
backgroundColor: '#55e9fe',
lineTension: 0,
fill: false,
}, {
data: Array(10).fill(0).map((value, index) => (index)), //data input ex) [1,2,3,4,...]
label: 'is no',
borderWidth: 1,
backgroundColor: '#5e90ff',
lineTension: 0,
fill: false,
}, {
data: Array(10).fill(0).map((value, index) => (index)), //data input ex) [1,2,3,4,...]
label: 'data',
borderWidth: 1,
backgroundColor: '#ffa081',
lineTension: 0,
fill: false,
}],
};
const chartOptions = {
responsive: true,
maintainAspectRatio: false,
legend: {
position: 'right',
display: window.innerHeight > 900 ? true : false,
},
scales: {
yAxes: [{
gridLines: {
display: true,
color: '#888',
},
}],
xAxes: [{
gridLine: {
display: true,
color: '#888',
},
}]
}
}
const onClickExcelDwonload = async () => {
// 그래프 캡쳐 // 안됨..
await html2canvas(document.querySelector("#graph")).then(canvas => {
let image = document.getElementById('graph-image');
image.src = canvas.toDataURL("image/png");
image.style.display='block';
image.style.width = '178px';
image.style.height = '188px';
});
const table = document.getElementById("export-div");
let tab_text = '<html xmlns:x="urn:schemas-microsoft-com:office:excel">';
tab_text += '<head><meta http-equiv="content-type" content="application/vnd.ms-excel; charset=UTF-8">';
tab_text += '<xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet>'
tab_text += `<x:Name>TestFileName</x:Name>`;
tab_text += '<x:WorksheetOptions><x:Panes></x:Panes></x:WorksheetOptions></x:ExcelWorksheet>';
tab_text += '</x:ExcelWorksheets></x:ExcelWorkbook></xml>';
tab_text += '<style> table { border: 1 solid black}; tr { border : 1 solid black}; </style>';
tab_text += '</head><body>';
// tab_text += "<table border='1px'>";
let exportTable = await table.cloneNode(true);
tab_text += exportTable.outerHTML;
// tab_text += '</table></body></html>';
tab_text += '</body></html>';
let data_type = 'data:application/vnd.ms-excel';
let ua = window.navigator.userAgent;
let msie = ua.indexOf("MSIE ");
let fileName = 'TestFileName.xls';
// browser
if (msie > 0 || !!navigator.userAgent.match(/Trident.*rv\:11\./)) {
//ie
if (window.navigator.msSaveBlob) {
let blob = new Blob([tab_text], {
type: "application/csv;charset=utf-8;"
});
navigator.msSaveBlob(blob, fileName);
}
} else { // etc
let blob2 = new Blob([tab_text], {
type: "application/csv;charset=utf-8;"
});
let filename = fileName;
let elem = window.document.createElement('a');
elem.href = window.URL.createObjectURL(blob2);
elem.download = filename;
document.body.appendChild(elem);
elem.click();
document.body.removeChild(elem);
}
}
return (
<div>
<button onClick={onClickExcelDwonload}>excel export</button>
<div id="export-div">
<img id="graph-image" src="#" style={{ display: 'none', width: '178px', height: '188px' }} />
<div id="graph">
chart
<Line data={chartData} options={chartOptions}></Line>
</div>
<div>
data table
<table>
<tr>
<th style={{ border: '1px solid black' }}>Day</th>
<th style={{ border: '1px solid black' }}>MON</th>
<th style={{ border: '1px solid black' }}>Tue</th>
<th style={{ border: '1px solid black' }}>Wed</th>
<th style={{ border: '1px solid black' }}>Thu</th>
<th style={{ border: '1px solid black' }}>Fri</th>
<th style={{ border: '1px solid black' }}>Sat</th>
<th style={{ border: '1px solid black' }}>Sun</th>
</tr>
<tr>
<td style={{ border: '1px solid black', textAlign: 'right' }}>1</td>
<td style={{ border: '1px solid black', textAlign: 'right' }}>1</td>
<td style={{ border: '1px solid black', textAlign: 'right' }}>2</td>
<td style={{ border: '1px solid black', textAlign: 'right' }}>3</td>
<td style={{ border: '1px solid black', textAlign: 'right' }}>4</td>
<td style={{ border: '1px solid black', textAlign: 'right' }}>5</td>
<td style={{ border: '1px solid black', textAlign: 'right' }}>6</td>
<td style={{ border: '1px solid black', textAlign: 'right' }}>7</td>
</tr>
<tr>
<td style={{ border: '1px solid black', textAlign: 'right' }}>1.1</td>
<td style={{ border: '1px solid black', textAlign: 'right' }}>1.1</td>
<td style={{ border: '1px solid black', textAlign: 'right' }}>2.1</td>
<td style={{ border: '1px solid black', textAlign: 'right' }}>3.1</td>
<td style={{ border: '1px solid black', textAlign: 'right' }}>4.1</td>
<td style={{ border: '1px solid black', textAlign: 'right' }}>5.1</td>
<td style={{ border: '1px solid black', textAlign: 'right' }}>6.1</td>
<td style={{ border: '1px solid black', textAlign: 'right' }}>7.1</td>
</tr>
</table>
</div>
</div>
</div>
)
}
export default Test
주의점? : 팝업창으로 띄워서 하는 경우 element를 인식 못하는 경우가 있었음...
>> react-new-window 라이브러리를 사용했었는데 id나 tag 등으로 element를 찾지 못함..
main 창에서 인식을해서 자식창의 element를 찾지 못함.
그래서 아래와 같이 Portal 을 이용해서 인식하게 만들어 보았는데
scss가 잘 안먹혔음... 그래서 팝업창은 일단 포기했음.
(내가 잘못한거겠지 ㅜㅜ 테스트와 Portal에 대한 공부가 더 필요함)
=========main.js====================
import React from 'react';
import MyWindowPortal from './sub';
export default class main {
render() {
return {
<div>
<button className={cx('button')} onClick={() => reportExcelDwonload()}>엑셀 다운로드</button>
<MyWindowPortal setContainerEl={(containerEl) => this.setState({ ...this.state, containerEl: containerEl})}> */}
<div> 내용 내용</div>
<div> 내용 </div>
</MyWindowPortal>
</div>
}
}
}
=======================================sub.js==============
import React from 'react';
import ReactDOM from 'react-dom';
export default class MyWindowPortal extends React.PureComponent {
constructor(props) {
super(props);
// STEP 1: create a container <div>
this.containerEl = document.createElement('div');
// 이건 출처에 없던건데 부모한테 element를 어떻게 넘겨줘야할지 모르겠어서 props로 el를 넘길 수 있는 함수를 받아 처리하였었음.
this.props.setContainerEl(this.containerEl);
this.externalWindow = null;
}
render() {
// STEP 2: append props.children to the container <div> that isn't mounted anywhere yet
return ReactDOM.createPortal(this.props.children, this.containerEl);
}
componentDidMount() {
// STEP 3: open a new browser window and store a reference to it
this.externalWindow = window.open('', '');
// STEP 4: append the container <div> (that has props.children appended to it) to the body of the new window
this.externalWindow.document.body.appendChild(this.containerEl);
}
componentWillUnmount() {
// STEP 5: This will fire when this.state.showWindowPortal in the parent component becomes false
// So we tidy up by closing the window
this.externalWindow.close();
}
}
출처 : 출처를 찾습니다.... 마구마구 찾아다니다가 출처를 꺼버려서 출처를 기입 못함.
반응형
'Web > React' 카테고리의 다른 글
[React] React 초보? 참고 사이트 추천 (0) | 2021.11.10 |
---|---|
[React] 소스 맵(Source Map) (0) | 2021.10.21 |
[React] GENERATE_SOURCEMAP=false build error (feat. windows 10) (0) | 2021.10.21 |
[ReactJS] react-chartjs-2 (0) | 2020.07.19 |
[TypeScript] React 프로젝트 초기 설정 (window) (mobx) (0) | 2020.05.15 |