/** * V.AI STUDIO — Excel & PDF Export Ultimate Fix v2.0 * ============================================== * FIX: File Excel chỉ tải được 1 lần, lần 2+ thất bại * * GIẢI PHÁP: * - KHÔNG revoke blob URL - cleanup khi tab đóng * - Tạo filename unique * - Sử dụng ExcelJS cho GH Excel để xuất .xlsx thật */ (function() { 'use strict'; // Registry quản lý blob URLs var blobRegistry = []; var isUnloading = false; // Cleanup khi tab đóng function cleanupBlobs() { isUnloading = true; blobRegistry.forEach(function(e) { if (!e.revoked && e.url) { try { URL.revokeObjectURL(e.url); e.revoked = true; } catch (err) {} } }); blobRegistry = []; } window.addEventListener('pagehide', cleanupBlobs); window.addEventListener('beforeunload', cleanupBlobs); window.addEventListener('unload', cleanupBlobs); // PATCH URL.revokeObjectURL - không revoke blob URLs khi chưa unload (function() { var origRevoke = URL.revokeObjectURL; URL.revokeObjectURL = function(url) { if (isUnloading || !url || typeof url !== 'string' || !url.startsWith('blob:')) { return origRevoke.apply(this, arguments); } var found = blobRegistry.find(function(e) { return e.url === url; }); if (!found) { blobRegistry.push({ url: url, revoked: false, created: Date.now() }); } }; })(); // Hàm download - KHÔNG revoke function downloadBlob(blob, filename) { var url = URL.createObjectURL(blob); blobRegistry.push({ url: url, revoked: false, created: Date.now() }); var a = document.createElement('a'); a.href = url; a.download = filename; a.style.cssText = 'position:fixed;top:-100px;left:-100px;width:1px;height:1px;'; document.body.appendChild(a); a.click(); setTimeout(function() { try { document.body.removeChild(a); } catch(e) {} }, 100); } // ===== FIX: exportDeliveryExcel ===== window.exportDeliveryExcel = async function(order, opt) { if (typeof ExcelJS === 'undefined') { if (typeof showToast === 'function') showToast('⚠️ Chưa có ExcelJS'); return; } var qd; if (order && order.items) { qd = { customer: { name: order.customer || '', phone: order.phone || '', addr: order.addr || '', date: order.date || '' }, items: (order.items || []).map(function(it, i) { return { stt: i + 1, name: it.name || '', model: it.model || '', qty: it.qty || 1, price: it.price || 0, note: it.note || '' }; }) }; } else if (window.cart && window.cart.length > 0) { qd = { customer: { name: '', phone: '', addr: '', date: new Date().toLocaleDateString('vi-VN') }, items: window.cart.map(function(item, idx) { var p = (window.D && D[item.idx]) ? D[item.idx] : null; return { stt: idx + 1, name: p ? (p.name || p.n || '') : (item.name || ''), model: p ? (p.sku || p.mod || '') : (item.sku || item.model || ''), qty: item.qty || 1, price: item.priceNum || item.price || 0, note: item.note || '' }; }) }; } else { if (typeof showToast === 'function') showToast('⚠️ Giỏ hàng trống!'); return; } var code = (order && order.code) ? order.code : (window.VAI_QR && window.VAI_QR.getEffectiveOrderCode ? window.VAI_QR.getEffectiveOrderCode() : 'VAS-GH'); try { var wb = new ExcelJS.Workbook(); var ws = wb.addWorksheet('Phiếu Giao Hàng'); ws.views = [{ showGridLines: false }]; ws.columns = [{ width: 5 }, { width: 34 }, { width: 14 }, { width: 8 }, { width: 20 }]; ws.mergeCells('A1:E1'); ws.getCell('A1').value = 'PHIẾU GIAO HÀNG V.AI STUDIO'; ws.getCell('A1').font = { bold: true, size: 14, color: { argb: 'FF003F62' } }; ws.getCell('A1').alignment = { horizontal: 'center' }; ws.getCell('A1').fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFF0F7FF' } }; ws.getRow(1).height = 26; ws.getCell('A2').value = 'Ngày giao:'; ws.getCell('B2').value = new Date().toLocaleDateString('vi-VN'); var hr = ws.getRow(4); ['STT', 'Tên sản phẩm', 'Mã SP', 'SL', 'Ghi chú'].forEach(function(h, i) { var c = hr.getCell(i + 1); c.value = h; c.font = { bold: true, color: { argb: 'FFFFFFFF' } }; c.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF003F62' } }; }); hr.height = 20; var cr = 5, totalQty = 0; (qd.items || []).forEach(function(it, i) { var row = ws.getRow(cr); row.values = [i + 1, it.name || '', it.model || '', it.qty || 1, it.note || '']; totalQty += (it.qty || 1); cr++; }); var tr = ws.getRow(cr); ws.mergeCells(cr, 1, cr, 3); ws.getCell('A' + cr).value = 'TỔNG CỘNG SỐ LƯỢNG'; ws.getCell('A' + cr).font = { bold: true, size: 10, color: { argb: 'FF1E40AF' } }; ws.getCell('D' + cr).value = totalQty; var buf = await wb.xlsx.writeBuffer(); var blob = new Blob([buf], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }); downloadBlob(blob, 'GH-' + code + '.xlsx'); if (typeof showToast === 'function') { showToast('✅ Đã tải GH-' + code + '.xlsx'); } } catch (e) { console.error('exportDeliveryExcel error:', e); if (typeof showToast === 'function') showToast('❌ Lỗi: ' + e.message); } }; console.log('[VAI Excel Fix v2.0] Multi-download enabled - blob cleanup on tab unload'); })();