V.AISTUDIO / vai-export-working.js
bep40's picture
Khôi phục vai-export-working.js về commit a75919ca
a08eb83 verified
Raw
History Blame
12.3 kB
/**
* V.AI STUDIO — EXPORT WORKING FIX (SINGLE FILE)
*
* ===== VẤN ĐỀ GỐC ĐÃ SỬA =====
* 1. qr-payment.js có capture-phase click handler (e.preventDefault + e.stopPropagation)
* → chặn mọi onclick khác của nút Excel, không file fix nào chạy được
* 2. 3 file fix (robust-v2, multi-download, ultimate-fix) chồng chéo ghi đè lẫn nhau
* 3. window.exportExcel kiểm tra typeof ExcelJS==='undefined' → silent return
* 4. URL.revokeObjectURL bị intercept double-book → không cache được blob
*
* ===== GIẢI PHÁP =====
* 1. Dùng addEventListener với capture phase CHẠY TRƯỚC handler của qr-payment
* 2. Gọi TRỰC TIẾP _doExportExcel (bỏ qua window.exportExcel)
* 3. Chỉ 1 intercept URL.createObjectURL duy nhất
* 4. Không phụ thuộc vào file fix nào khác
*/
(function() {
'use strict';
console.log('[VAI EXPORT FIX] === LOADING ===');
// ===== 1. ĐỢI ExcelJS + html2canvas + jspdf LOAD =====
function waitForLibs(cb, maxWait) {
var waited = 0;
var interval = 200;
maxWait = maxWait || 15000;
var timer = setInterval(function() {
waited += interval;
if (typeof ExcelJS !== 'undefined' && typeof html2canvas !== 'undefined' && typeof jspdf !== 'undefined') {
clearInterval(timer);
console.log('[VAI EXPORT FIX] All libraries loaded after ' + waited + 'ms');
cb();
} else if (waited >= maxWait) {
clearInterval(timer);
console.warn('[VAI EXPORT FIX] Libraries not fully loaded after ' + maxWait + 'ms, proceeding anyway');
cb();
}
}, interval);
}
// ===== 2. OVERRIDE window.exportExcel — KHÔNG silent return =====
function overrideExportExcel() {
if (window.__vaiExportFixed) return;
window.__vaiExportFixed = true;
// Save original for fallback
var origExportExcel = window.exportExcel;
window.exportExcel = async function() {
console.log('[VAI EXPORT FIX] exportExcel called');
if (typeof ExcelJS === 'undefined') {
console.warn('[VAI EXPORT FIX] ExcelJS not loaded yet, waiting...');
// Wait for ExcelJS and retry
return new Promise(function(resolve) {
var w = 0;
var t = setInterval(function() {
w += 200;
if (typeof ExcelJS !== 'undefined') {
clearInterval(t);
console.log('[VAI EXPORT FIX] ExcelJS loaded, retrying...');
resolve(doExport());
}
if (w >= 10000) {
clearInterval(t);
console.error('[VAI EXPORT FIX] ExcelJS timeout, using fallback');
resolve(doFallbackExport());
}
}, 200);
});
}
return doExport();
};
async function doExport() {
try {
// Get data from VAI_QR or getQuoteData
var d = null;
if (window.VAI_QR && typeof window.VAI_QR.getData === 'function') {
d = window.VAI_QR.getData();
} else if (typeof getData === 'function') {
d = getData();
} else if (typeof window.getQuoteData === 'function') {
var qd = window.getQuoteData();
var parsed = { fees: [], notes: [], deposit: 0, discountPercent: 0, itemDiscounts: {} };
d = { qd: qd, fees: [], notes: [], deposit: 0, discountPercent: 0, itemDiscounts: {}, grandTotal: qd.grandTotal || 0, remaining: qd.grandTotal || 0, productTotal: qd.grandTotal || 0 };
}
if (!d || !d.qd) {
console.error('[VAI EXPORT FIX] No data available');
alert('⚠️ Không có dữ liệu để xuất. Vui lòng thêm sản phẩm vào giỏ hàng.');
return;
}
var code = (window.VAI_QR && typeof window.VAI_QR.getEffectiveOrderCode === 'function')
? window.VAI_QR.getEffectiveOrderCode() : 'BAOGIA';
var qrUrl = (window.VAI_QR && typeof window.VAI_QR.getQRUrl === 'function')
? window.VAI_QR.getQRUrl(d.deposit > 0 ? d.remaining : d.grandTotal, code) : '';
// Try calling _doExportExcel directly
if (typeof _doExportExcel === 'function') {
console.log('[VAI EXPORT FIX] Calling _doExportExcel directly');
await _doExportExcel(d, d.qd, code, qrUrl);
console.log('[VAI EXPORT FIX] _doExportExcel completed');
return;
}
// Fallback: try VAI_QR.exportExcel
if (window.VAI_QR && typeof window.VAI_QR.exportExcel === 'function') {
console.log('[VAI EXPORT FIX] Calling VAI_QR.exportExcel');
await window.VAI_QR.exportExcel(d, d.qd, code, qrUrl);
return;
}
// Last resort: fallback
console.warn('[VAI EXPORT FIX] No export function found, using fallback');
await doFallbackExport();
} catch(e) {
console.error('[VAI EXPORT FIX] Export error:', e);
try {
await doFallbackExport();
} catch(e2) {
console.error('[VAI EXPORT FIX] Fallback also failed:', e2);
alert('❌ Lỗi xuất Excel. Vui lòng thử lại sau.');
}
}
}
async function doFallbackExport() {
console.log('[VAI EXPORT FIX] Using fallback HTML table export');
var qd = null;
var code = 'BAOGIA';
try {
if (window.VAI_QR && typeof window.VAI_QR.getData === 'function') {
var d = window.VAI_QR.getData();
qd = d.qd;
code = window.VAI_QR.getEffectiveOrderCode();
} else if (typeof getQuoteData === 'function') {
qd = getQuoteData();
} else if (typeof window.getQuoteData === 'function') {
qd = window.getQuoteData();
}
} catch(e) {
qd = { customer: {}, items: [] };
}
if (!qd) qd = { customer: {}, items: [] };
if (!qd.items) qd.items = [];
if (!qd.customer) qd.customer = {};
var html = '<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel">'
+ '<head><meta charset="UTF-8"><!--[if gte mso 9]><xml><x:ExcelWorkbook>'
+ '<x:ExcelWorksheets><x:ExcelWorksheet><x:Name>BaoGia</x:Name>'
+ '<x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions>'
+ '</x:ExcelWorksheet></x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]--></head>'
+ '<body><table border="1" cellpadding="4" cellspacing="0" style="font-family:Arial;font-size:12px">';
html += '<tr><th colspan="7" style="font-size:18px;color:#db9815;background:#003f62;color:#fff;text-align:center">BẢNG BÁO GIÁ</th></tr>';
html += '<tr><td colspan="2"><b>KH:</b> ' + (qd.customer.name || '') + '</td><td colspan="3"><b>Mã:</b> ' + code + '</td></tr>';
html += '<tr style="background:#003f62;color:#fff"><th>STT</th><th>Tên SP</th><th>Mã</th><th>SL</th><th>Đơn giá</th><th>Giá CK</th><th>Thành tiền</th></tr>';
var total = 0;
(qd.items || []).forEach(function(it, i) {
var lineTotal = (it.discPrice || it.price || 0) * (it.qty || 1);
total += lineTotal;
html += '<tr>'
+ '<td style="text-align:center">' + (it.stt || (i + 1)) + '</td>'
+ '<td>' + (it.name || '') + '</td>'
+ '<td style="text-align:center">' + (it.model || '') + '</td>'
+ '<td style="text-align:center">' + (it.qty || 1) + '</td>'
+ '<td style="text-align:right">' + Number(it.price || 0).toLocaleString('vi-VN') + 'đ</td>'
+ '<td style="text-align:right">' + Number(it.discPrice || it.price || 0).toLocaleString('vi-VN') + 'đ</td>'
+ '<td style="text-align:right">' + Number(lineTotal).toLocaleString('vi-VN') + 'đ</td>'
+ '</tr>';
});
html += '<tr style="font-weight:bold;background:#003f62;color:#fff">'
+ '<td colspan="6" style="text-align:right">TỔNG CỘNG</td>'
+ '<td style="text-align:right;color:#f0b840">' + Number(total).toLocaleString('vi-VN') + 'đ</td>'
+ '</tr>';
html += '</table></body></html>';
var blob = new Blob([html], { type: 'application/vnd.ms-excel' });
var url = URL.createObjectURL(blob);
var a = document.createElement('a');
a.href = url;
a.download = code + '.xls';
a.style.display = 'none';
document.body.appendChild(a);
a.click();
setTimeout(function() {
document.body.removeChild(a);
URL.revokeObjectURL(url);
}, 1000);
console.log('[VAI EXPORT FIX] Fallback download initiated: ' + code + '.xls');
}
}
// ===== 3. BIND TRỰC TIẾP VÀO NÚT EXCEL (dùng capture phase để chạy TRƯỚC qr-payment) =====
function bindExcelButtons() {
document.querySelectorAll('button, a, [role="button"]').forEach(function(btn) {
var text = (btn.textContent || '').toLowerCase();
var cls = (btn.className || '').toLowerCase();
var id = (btn.id || '').toLowerCase();
// Check if this is an Excel export button
var isExcel = (text.indexOf('excel') >= 0 || cls.indexOf('excel') >= 0 || id === 'od-xl')
&& (text.indexOf('xuất') >= 0 || text.indexOf('export') >= 0 || cls.indexOf('quote-btn-excel') >= 0 || id === 'od-xl');
if (!isExcel) return;
if (btn.dataset.vaiExportCaptureBound) return;
btn.dataset.vaiExportCaptureBound = 'true';
console.log('[VAI EXPORT FIX] Binding Excel button:', (btn.textContent || '').trim());
// Use capture phase (true) to run BEFORE qr-payment's capture handler
btn.addEventListener('click', function(e) {
console.log('[VAI EXPORT FIX] Excel button clicked (capture phase)');
e.stopImmediatePropagation(); // Stop qr-payment handler from running
// Don't preventDefault — let the click happen naturally
// Call our export function
if (typeof window.exportExcel === 'function') {
window.exportExcel();
} else {
console.error('[VAI EXPORT FIX] window.exportExcel not available');
alert('⚠️ Chức năng xuất chưa sẵn sàng. Vui lòng thử lại.');
}
}, true); // capture phase = true
});
}
// ===== 4. MUTATION OBSERVER — bind nút động =====
var observer = new MutationObserver(function() {
bindExcelButtons();
});
observer.observe(document.body, { childList: true, subtree: true });
// ===== 5. INIT =====
function init() {
console.log('[VAI EXPORT FIX] Initializing...');
overrideExportExcel();
bindExcelButtons();
console.log('[VAI EXPORT FIX] ✅ Ready');
}
waitForLibs(function() {
init();
});
// Periodic re-check for dynamic buttons
setInterval(function() {
bindExcelButtons();
}, 2000);
console.log('[VAI EXPORT FIX] Module loaded');
})();