본문 바로가기

Web/React

[REACT] javascript excel export (xml 을 이용한 excel export / )

반응형

다양한 방법이 있겠지만

 

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();
    }
}

 

출처 : 출처를 찾습니다.... 마구마구 찾아다니다가 출처를 꺼버려서 출처를 기입 못함.

반응형