| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| (function() { |
| 'use strict'; |
| |
| |
| var blobRegistry = []; |
| var isUnloading = false; |
| |
| |
| 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); |
| |
| |
| (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() }); |
| } |
| }; |
| })(); |
| |
| |
| 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); |
| } |
| |
| |
| 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'); |
| })(); |