V.AISTUDIO / excel-multi-fix-complete.js
bep40's picture
Upload excel-multi-fix-complete.js
5d7db9c verified
Raw
History Blame
10.4 kB
/**
* V.AI STUDIO — Excel Multi-Download Complete Fix
* ============================================
* FIX TRIỆT ĐỐI cho vấn đề: File Excel chỉ tải được 1 lần
*
* Áp dụng cho:
* - Modal "Lập báo giá" -> nút 💾 Excel
* - Modal "Đơn hàng" -> nút 💾 Lưu, GH Excel
*/
(function() {
'use strict';
// Registry quản lý blob URLs
var blobRegistry = [];
var isUnloading = false;
// Cleanup khi tab đóng
function cleanupAllBlobs() {
isUnloading = true;
blobRegistry.forEach(function(entry) {
if (!entry.revoked && entry.url) {
try { URL.revokeObjectURL(entry.url); entry.revoked = true; } catch (e) {}
}
});
blobRegistry = [];
}
window.addEventListener('pagehide', cleanupAllBlobs);
window.addEventListener('beforeunload', cleanupAllBlobs);
window.addEventListener('unload', cleanupAllBlobs);
// PATCH URL.revokeObjectURL - không revoke blob URLs khi chưa unload
(function() {
var origRevoke = URL.revokeObjectURL;
URL.revokeObjectURL = function(url) {
if (isUnloading || !url || !url.startsWith('blob:')) {
return origRevoke.apply(this, arguments);
}
// Chỉ đăng ký, không revoke
var found = blobRegistry.find(function(e) { return e.url === url; });
if (!found) {
blobRegistry.push({ url: url, revoked: false, created: Date.now() });
}
};
})();
// PATCH exportExcel - Báo giá
var origExportExcel = window.exportExcel;
window.exportExcel = async function() {
if (typeof ExcelJS === 'undefined') {
if (typeof showToast === 'function') showToast('⚠️ Chưa có ExcelJS');
return;
}
// Lấy dữ liệu
var d, qd, code, qrUrl;
if (window.VAI_QR && typeof window.VAI_QR.getData === 'function') {
d = window.VAI_QR.getData();
qd = d.qd;
code = window.VAI_QR.getEffectiveOrderCode();
qrUrl = window.VAI_QR.getQRUrl(d.remaining || d.grandTotal, code);
} else if (window._origGetQuoteData) {
qd = window._origGetQuoteData();
code = 'VAS-' + Date.now();
d = { grandTotal: qd.grandTotal || 0, remaining: qd.grandTotal || 0 };
} else {
if (typeof showToast === 'function') showToast('⚠️ Lỗi dữ liệu');
return;
}
// TẠO MỚI hoàn toàn
var wb = new ExcelJS.Workbook();
var ws = wb.addWorksheet('Báo giá');
ws.views = [{ showGridLines: false }];
ws.columns = [{ width: 5 }, { width: 34 }, { width: 18 }, { width: 10 }, { width: 15 }];
// Header
ws.mergeCells('A1:E1');
ws.getCell('A1').value = 'BẢNG BÁO GIÁ V.AI STUDIO';
ws.getCell('A1').font = { bold: true, size: 16, color: { argb: 'FF003F62' } };
ws.getCell('A1').alignment = { horizontal: 'center', vertical: 'middle' };
ws.getCell('A1').fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFF0F7FF' } };
// Customer info
ws.getCell('A2').value = 'Khách hàng:';
ws.mergeCells('B2:D2');
ws.getCell('B2').value = (qd.customer ? qd.customer.name : '') || '';
ws.getCell('A3').value = 'SĐT:';
ws.getCell('B3').value = (qd.customer ? qd.customer.phone : '') || '';
ws.getCell('D3').value = 'Mã đơn: ' + code;
ws.getCell('A4').value = 'Địa chỉ:';
ws.mergeCells('B4:E4');
ws.getCell('B4').value = (qd.customer ? qd.customer.addr : '') || '';
// Header row
var hr = ws.getRow(6);
['STT', 'Tên sản phẩm', 'Mã SP', 'SL', 'Giá'].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' } };
});
// Items
var cr = 7, total = 0;
(qd.items || []).forEach(function(it, i) {
var row = ws.getRow(cr);
row.values = [i + 1, it.name || '', it.model || '', it.qty || 1, (it.discPrice || it.price || 0) * (it.qty || 1)];
if ((it.discPrice || it.price) > 0) {
row.getCell(5).numFmt = '#,##0"đ"';
}
row.getCell(5).font = { bold: true };
total += (it.discPrice || it.price || 0) * (it.qty || 1);
cr++;
});
// Total
ws.mergeCells(cr, 1, cr, 4);
ws.getCell('A' + cr).value = 'TỔNG CỘNG';
ws.getCell('A' + cr).font = { bold: true, size: 12 };
ws.getCell('E' + cr).value = total;
ws.getCell('E' + cr).font = { bold: true, size: 14, color: { argb: 'FF003F62' } };
ws.getCell('E' + cr).numFmt = '#,##0"đ"';
// Download - KHÔNG revoke
var buf = await wb.xlsx.writeBuffer();
var blob = new Blob([buf], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
var url = URL.createObjectURL(blob);
// Đăng ký để cleanup khi unload
blobRegistry.push({ url: url, revoked: false, created: Date.now() });
var a = document.createElement('a');
a.href = url;
a.download = code + '.xlsx';
document.body.appendChild(a);
a.click();
setTimeout(function() { document.body.removeChild(a); }, 100);
if (typeof showToast === 'function') {
showToast('✅ Đã tải ' + code + '.xlsx');
}
};
// PATCH exportDeliveryExcel - Giao hàng
var origExportDeliveryExcel = window.exportDeliveryExcel;
window.exportDeliveryExcel = async function(order, opt) {
if (typeof ExcelJS === 'undefined') {
if (typeof showToast === 'function') showToast('⚠️ Chưa có ExcelJS');
return;
}
// Lấy dữ liệu qd
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,
note: it.note || ''
};
})
};
} else if (window._origGetQuoteData) {
qd = window._origGetQuoteData();
} else if (window.VAI_QR && window.VAI_QR.getData) {
qd = window.VAI_QR.getData().qd;
} else {
if (typeof showToast === 'function') showToast('⚠️ Lỗi dữ liệu');
return;
}
var code = (order && order.code) ? order.code :
(window.VAI_QR ? window.VAI_QR.getEffectiveOrderCode() : 'VAS-GH');
// TẠO MỚI hoàn toàn
var wb = new ExcelJS.Workbook();
var ws = wb.addWorksheet('Giao hàng');
ws.views = [{ showGridLines: false }];
ws.columns = [{ width: 5 }, { width: 34 }, { width: 14 }, { width: 8 }, { width: 20 }];
// Header
ws.mergeCells('A1:E1');
ws.getCell('A1').value = 'PHIẾU GIAO HÀNG';
ws.getCell('A1').font = { bold: true, size: 16, color: { argb: 'FF003F62' } };
ws.getCell('A1').alignment = { horizontal: 'center' };
ws.getCell('A1').fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFF0F7FF' } };
// Customer info
ws.getCell('A2').value = 'KH: ' + (qd.customer ? qd.customer.name : '');
ws.getCell('D2').value = 'Mã: ' + code;
ws.getCell('A3').value = 'SĐT: ' + (qd.customer ? qd.customer.phone : '');
ws.getCell('D3').value = 'Ngày: ' + (qd.customer ? (qd.customer.date || '') : '');
ws.getCell('A4').value = 'Địa chỉ: ' + (qd.customer ? (qd.customer.addr || '') : '');
// Header row
var hr = ws.getRow(6);
['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' } };
});
// Items
var cr = 7, 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++;
});
// Total
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('D' + cr).value = totalQty;
// Signature
cr += 2;
ws.getCell('A' + cr).value = 'Bên giao: ________________';
ws.getCell('D' + cr).value = 'Bên nhận: ________________';
// Download - KHÔNG revoke
var buf = await wb.xlsx.writeBuffer();
var blob = new Blob([buf], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
var url = URL.createObjectURL(blob);
// Đăng ký để cleanup khi unload
blobRegistry.push({ url: url, revoked: false, created: Date.now() });
var a = document.createElement('a');
a.href = url;
a.download = 'GH-' + code + '.xlsx';
document.body.appendChild(a);
a.click();
setTimeout(function() { document.body.removeChild(a); }, 100);
if (typeof showToast === 'function') {
showToast('✅ Đã tải GH-' + code + '.xlsx');
}
};
console.log('[Excel Multi-Fix Complete] Loaded - Supports unlimited downloads!');
})();