엔지셉트플러스액 매출추이

💻 HTML 2025-12-23
수정
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Enzycept Sales Chart Generator</title>
    <script src="https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.js"></script>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            padding: 20px;
        }
        
        .container {
            max-width: 1400px;
            margin: 0 auto;
            background: white;
            border-radius: 20px;
            box-shadow: 0 20px 60px rgba(0,0,0,0.3);
            padding: 40px;
        }
        
        h1 {
            color: #333;
            text-align: center;
            margin-bottom: 10px;
            font-size: 32px;
        }
        
        .subtitle {
            text-align: center;
            color: #666;
            margin-bottom: 30px;
            font-size: 16px;
        }
        
        .upload-section {
            background: #f8f9ff;
            border-radius: 15px;
            padding: 30px;
            margin-bottom: 30px;
        }
        
        .upload-area {
            border: 3px dashed #667eea;
            border-radius: 15px;
            padding: 40px;
            text-align: center;
            background: white;
            cursor: pointer;
            transition: all 0.3s;
        }
        
        .upload-area:hover {
            background: #f0f2ff;
            border-color: #764ba2;
        }
        
        .upload-area.dragover {
            background: #e8ebff;
            border-color: #764ba2;
            transform: scale(1.02);
        }
        
        .upload-icon {
            font-size: 48px;
            margin-bottom: 15px;
        }
        
        input[type="file"] {
            display: none;
        }
        
        .btn {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            border: none;
            padding: 15px 40px;
            border-radius: 10px;
            font-size: 16px;
            font-weight: bold;
            cursor: pointer;
            transition: all 0.3s;
            margin-top: 20px;
        }
        
        .btn:hover {
            transform: translateY(-2px);
            box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
        }
        
        .btn:disabled {
            background: #ccc;
            cursor: not-allowed;
            transform: none;
        }
        
        .stats-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
            gap: 20px;
            margin-bottom: 30px;
        }
        
        .stat-card {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 25px;
            border-radius: 15px;
            box-shadow: 0 5px 15px rgba(0,0,0,0.1);
        }
        
        .stat-card h3 {
            font-size: 14px;
            opacity: 0.9;
            margin-bottom: 10px;
        }
        
        .stat-card .value {
            font-size: 28px;
            font-weight: bold;
            margin-bottom: 5px;
        }
        
        .stat-card .detail {
            font-size: 14px;
            opacity: 0.8;
        }
        
        .charts-section {
            margin-top: 30px;
        }
        
        .chart-container {
            background: #f8f9ff;
            border-radius: 15px;
            padding: 30px;
            margin-bottom: 30px;
        }
        
        .chart-title {
            font-size: 24px;
            font-weight: bold;
            color: #333;
            margin-bottom: 20px;
            text-align: center;
        }
        
        canvas {
            max-height: 500px;
        }
        
        .status {
            padding: 15px;
            border-radius: 10px;
            margin-top: 20px;
            text-align: center;
            display: none;
        }
        
        .status.success {
            background: #d4edda;
            color: #155724;
            border: 1px solid #c3e6cb;
        }
        
        .status.error {
            background: #f8d7da;
            color: #721c24;
            border: 1px solid #f5c6cb;
        }
        
        .status.loading {
            background: #d1ecf1;
            color: #0c5460;
            border: 1px solid #bee5eb;
        }
        
        .spinner {
            border: 3px solid #f3f3f3;
            border-top: 3px solid #667eea;
            border-radius: 50%;
            width: 30px;
            height: 30px;
            animation: spin 1s linear infinite;
            margin: 10px auto;
        }
        
        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }
        
        .download-section {
            text-align: center;
            margin-top: 30px;
        }
        
        .download-btn {
            background: #2ecc71;
            margin: 10px;
            display: inline-block;
        }
        
        .hidden {
            display: none;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>📊 Enzycept Plus Solution</h1>
        <p class="subtitle">Sales Chart Generator - 엔지셉트플러스액 3년 매출 분석</p>
        
        <div class="upload-section">
            <div class="upload-area" id="uploadArea">
                <div class="upload-icon">📁</div>
                <h3>Excel 파일을 선택하거나 드래그하세요</h3>
                <p style="color: #666; margin-top: 10px;">지원 형식: .xlsx, .xls</p>
            </div>
            <input type="file" id="fileInput" accept=".xlsx,.xls">
            <div style="text-align: center;">
                <button class="btn" id="generateBtn" disabled>차트 생성</button>
            </div>
            <div class="status" id="status"></div>
        </div>
        
        <div id="resultsSection" class="hidden">
            <h2 style="margin-bottom: 20px;">📈 분석 결과</h2>
            
            <div class="stats-grid" id="statsGrid"></div>
            
            <div class="charts-section">
                <div class="chart-container">
                    <h3 class="chart-title">월별 매출액 추이</h3>
                    <canvas id="salesChart"></canvas>
                </div>
                
                <div class="chart-container">
                    <h3 class="chart-title">월별 판매수량 추이</h3>
                    <canvas id="quantityChart"></canvas>
                </div>
                
                <div class="chart-container">
                    <h3 class="chart-title">연간 매출 비교</h3>
                    <canvas id="annualChart"></canvas>
                </div>
            </div>
            
            <div class="download-section">
                <button class="btn download-btn" onclick="downloadCharts()">📥 차트 다운로드 (PNG)</button>
            </div>
        </div>
    </div>
    
    <script>
        const uploadArea = document.getElementById('uploadArea');
        const fileInput = document.getElementById('fileInput');
        const generateBtn = document.getElementById('generateBtn');
        const status = document.getElementById('status');
        const resultsSection = document.getElementById('resultsSection');
        
        let selectedFile = null;
        let statsData = null;
        let charts = {};
        
        // 파일 선택 이벤트
        uploadArea.addEventListener('click', () => fileInput.click());
        fileInput.addEventListener('change', (e) => handleFile(e.target.files[0]));
        
        // 드래그 앤 드롭
        uploadArea.addEventListener('dragover', (e) => {
            e.preventDefault();
            uploadArea.classList.add('dragover');
        });
        
        uploadArea.addEventListener('dragleave', () => {
            uploadArea.classList.remove('dragover');
        });
        
        uploadArea.addEventListener('drop', (e) => {
            e.preventDefault();
            uploadArea.classList.remove('dragover');
            handleFile(e.dataTransfer.files[0]);
        });
        
        function handleFile(file) {
            if (!file) return;
            
            if (!file.name.match(/\.(xlsx|xls)$/)) {
                showStatus('Excel 파일(.xlsx, .xls)을 선택해주세요.', 'error');
                return;
            }
            
            selectedFile = file;
            uploadArea.innerHTML = `
                <div class="upload-icon">✅</div>
                <h3>${file.name}</h3>
                <p style="color: #666; margin-top: 10px;">파일 크기: ${(file.size / 1024).toFixed(2)} KB</p>
            `;
            generateBtn.disabled = false;
            hideStatus();
        }
        
        generateBtn.addEventListener('click', processFile);
        
        function processFile() {
            if (!selectedFile) return;
            
            showStatus('데이터 분석 중...', 'loading');
            generateBtn.disabled = true;
            
            const reader = new FileReader();
            reader.onload = function(e) {
                try {
                    const data = new Uint8Array(e.target.result);
                    const workbook = XLSX.read(data, {type: 'array'});
                    
                    // '3년 추이' 시트 찾기
                    const sheetName = workbook.SheetNames.find(name => name.includes('3년 추이'));
                    if (!sheetName) {
                        throw new Error("'3년 추이' 시트를 찾을 수 없습니다.");
                    }
                    
                    const worksheet = workbook.Sheets[sheetName];
                    const jsonData = XLSX.utils.sheet_to_json(worksheet, {header: 1});
                    
                    // 데이터 추출 및 처리
                    statsData = processExcelData(jsonData);
                    
                    // 결과 표시
                    displayResults();
                    
                    showStatus('차트 생성 완료!', 'success');
                    
                } catch (error) {
                    showStatus('오류: ' + error.message, 'error');
                    generateBtn.disabled = false;
                }
            };
            
            reader.readAsArrayBuffer(selectedFile);
        }
        
        function processExcelData(data) {
            const years = [2023, 2024, 2025];
            const months = ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'];
            
            const salesData = {};
            const quantityData = {};
            
            // 데이터 추출 (row 3, 4, 5가 2023, 2024, 2025)
            years.forEach((year, yearIdx) => {
                const rowIdx = yearIdx + 3;
                salesData[year] = [];
                quantityData[year] = [];
                
                for (let monthIdx = 0; monthIdx < 12; monthIdx++) {
                    const colIdx = 1 + (monthIdx * 2);
                    const sales = parseFloat(data[rowIdx][colIdx]) || 0;
                    const qty = parseFloat(data[rowIdx][colIdx + 1]) || 0;
                    
                    salesData[year].push(sales);
                    quantityData[year].push(qty);
                }
            });
            
            // 연간 합계
            const annualSales = {};
            const annualQty = {};
            years.forEach(year => {
                annualSales[year] = salesData[year].reduce((a, b) => a + b, 0);
                annualQty[year] = quantityData[year].reduce((a, b) => a + b, 0);
            });
            
            // 성장률
            const growth2024 = ((annualSales[2024] - annualSales[2023]) / annualSales[2023] * 100).toFixed(1);
            const growth2025 = ((annualSales[2025] - annualSales[2024]) / annualSales[2024] * 100).toFixed(1);
            
            return {
                salesData,
                quantityData,
                annualSales,
                annualQty,
                growth2024,
                growth2025
            };
        }
        
        function displayResults() {
            resultsSection.classList.remove('hidden');
            
            // 통계 카드 생성
            const statsGrid = document.getElementById('statsGrid');
            statsGrid.innerHTML = `
                <div class="stat-card">
                    <h3>2023년 총 매출</h3>
                    <div class="value">₩${statsData.annualSales[2023].toLocaleString()}</div>
                    <div class="detail">${statsData.annualQty[2023].toLocaleString()} 개</div>
                </div>
                <div class="stat-card">
                    <h3>2024년 총 매출</h3>
                    <div class="value">₩${statsData.annualSales[2024].toLocaleString()}</div>
                    <div class="detail">성장률: ${statsData.growth2024 > 0 ? '+' : ''}${statsData.growth2024}%</div>
                </div>
                <div class="stat-card">
                    <h3>2025년 총 매출</h3>
                    <div class="value">₩${statsData.annualSales[2025].toLocaleString()}</div>
                    <div class="detail">성장률: ${statsData.growth2025 > 0 ? '+' : ''}${statsData.growth2025}%</div>
                </div>
            `;
            
            // 차트 생성
            createCharts();
            
            // 결과 섹션으로 스크롤
            resultsSection.scrollIntoView({ behavior: 'smooth' });
        }
        
        function createCharts() {
            const months = ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'];
            const years = [2023, 2024, 2025];
            const colors = ['#3498db', '#2ecc71', '#e74c3c'];
            
            // 기존 차트 제거
            Object.values(charts).forEach(chart => chart && chart.destroy());
            
            // 월별 매출액 차트
            const salesCtx = document.getElementById('salesChart').getContext('2d');
            charts.sales = new Chart(salesCtx, {
                type: 'bar',
                data: {
                    labels: months,
                    datasets: years.map((year, idx) => ({
                        label: `${year}년`,
                        data: statsData.salesData[year].map(v => v / 1000000),
                        backgroundColor: colors[idx] + 'CC',
                        borderColor: colors[idx],
                        borderWidth: 2
                    }))
                },
                options: {
                    responsive: true,
                    maintainAspectRatio: true,
                    plugins: {
                        legend: { display: true, position: 'top' },
                        title: { display: false }
                    },
                    scales: {
                        y: {
                            beginAtZero: true,
                            title: { display: true, text: '매출액 (백만원)' }
                        }
                    }
                }
            });
            
            // 월별 수량 차트
            const qtyCtx = document.getElementById('quantityChart').getContext('2d');
            charts.quantity = new Chart(qtyCtx, {
                type: 'line',
                data: {
                    labels: months,
                    datasets: years.map((year, idx) => ({
                        label: `${year}년`,
                        data: statsData.quantityData[year],
                        borderColor: colors[idx],
                        backgroundColor: colors[idx] + '33',
                        borderWidth: 3,
                        fill: true,
                        tension: 0.4
                    }))
                },
                options: {
                    responsive: true,
                    maintainAspectRatio: true,
                    plugins: {
                        legend: { display: true, position: 'top' }
                    },
                    scales: {
                        y: {
                            beginAtZero: true,
                            title: { display: true, text: '판매수량 (개)' }
                        }
                    }
                }
            });
            
            // 연간 비교 차트
            const annualCtx = document.getElementById('annualChart').getContext('2d');
            charts.annual = new Chart(annualCtx, {
                type: 'bar',
                data: {
                    labels: years.map(y => `${y}년`),
                    datasets: [{
                        label: '연간 매출액',
                        data: years.map(y => statsData.annualSales[y] / 1000000),
                        backgroundColor: colors.map(c => c + 'CC'),
                        borderColor: colors,
                        borderWidth: 2
                    }]
                },
                options: {
                    responsive: true,
                    maintainAspectRatio: true,
                    plugins: {
                        legend: { display: false }
                    },
                    scales: {
                        y: {
                            beginAtZero: true,
                            title: { display: true, text: '매출액 (백만원)' }
                        }
                    }
                }
            });
        }
        
        function downloadCharts() {
            Object.keys(charts).forEach((key, idx) => {
                const canvas = charts[key].canvas;
                const link = document.createElement('a');
                link.download = `enzycept_${key}_chart.png`;
                link.href = canvas.toDataURL();
                link.click();
            });
            
            showStatus('차트 다운로드 완료!', 'success');
        }
        
        function showStatus(message, type) {
            status.innerHTML = message;
            if (type === 'loading') {
                status.innerHTML += '<div class="spinner"></div>';
            }
            status.className = 'status ' + type;
            status.style.display = 'block';
        }
        
        function hideStatus() {
            status.style.display = 'none';
        }
    </script>
</body>
</html>
상세 정보
생성일: 2025-12-23
수정일: 2025-12-24
이 아이템이 링크하는 문서

링크된 문서가 없습니다.

이 아이템을 참조하는 문서

참조하는 문서가 없습니다.

액션
수정
공유 & 관리
복제
목록으로 메인으로
마지막 수정: 2025-12-24 13:17
이미지 URL 수정됨