Sửa qr-payment-v1039.js thêm blob registry
#15
by bep40 - opened
- qr-payment-v1039.js +92 -40
qr-payment-v1039.js
CHANGED
|
@@ -1,17 +1,54 @@
|
|
| 1 |
-
/* V.AI STUDIO — QR Payment / Quote Data v1039
|
| 2 |
-
* FIX TRIỆT ĐỐI: Multi-download Excel -
|
| 3 |
-
* Thay thế v1038 - xóa 2 dòng setTimeout revoke URL gây lỗi tải 2 lần
|
| 4 |
*/
|
| 5 |
-
(function(){'use strict';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
|
| 7 |
// Cleanup khi tab đóng
|
| 8 |
-
function cleanupBlobs(){
|
| 9 |
-
window.
|
| 10 |
-
|
| 11 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
|
| 13 |
-
//
|
| 14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
|
| 16 |
var BANK_ID='vib',BANK_ACCOUNT='918258385',BANK_NAME='TRAN QUOC VUONG',BANK_NAME_VN='Trần Quốc Vương';
|
| 17 |
function stripDiacritics(s){return s.normalize('NFD').replace(/[\u0300-\u036f]/g,'').replace(/[Đđ]/g,'D');}
|
|
@@ -44,14 +81,14 @@ function buildQuoteHTML(d,code,qrUrl){var qd=d.qd;var rows=qd.items.map(function
|
|
| 44 |
|
| 45 |
function buildDeliveryHTML(qd,code){var rows=(qd.items||[]).map(function(it,i){return '<tr style="border-bottom:1px solid #e2e8f0"><td style="padding:8px 4px;text-align:center;font-size:12px">'+(it.stt||(i+1))+'</td><td style="padding:8px 4px;font-weight:600;font-size:12px">'+(it.name||'')+'</td><td style="padding:8px 4px;text-align:center;font-size:11px">'+(it.model||'')+'</td><td style="padding:8px 4px;text-align:center;font-weight:700;font-size:13px">'+(it.qty||1)+'</td><td style="padding:8px 4px;font-size:11px">'+(it.note||'')+'</td></tr>';}).join('');return '<div style="text-align:center;margin-bottom:14px"><div style="font-size:20px;font-weight:900;color:#003f62">PHIẾU GIAO HÀNG</div></div><div style="margin-bottom:12px;font-size:11px;line-height:1.6"><b>Khách hàng:</b> '+(qd.customer?qd.customer.name:'')+' | <b>SĐT:</b> '+(qd.customer?qd.customer.phone:'')+'<br><b>Địa chỉ:</b> '+(qd.customer?qd.customer.addr||'':'')+'<br><b>Mã đơn:</b> '+code+' | <b>Ngày:</b> '+(qd.customer?qd.customer.date||'':'')+'</div><table style="width:100%;border-collapse:collapse;font-size:12px"><thead><tr style="background:#003f62;color:#fff"><th style="padding:8px 4px;width:35px">STT</th><th style="padding:8px 4px;text-align:left">Tên sản phẩm</th><th style="padding:8px 4px;width:90px">Mã SP</th><th style="padding:8px 4px;width:40px">SL</th><th style="padding:8px 4px;text-align:left;width:120px">Ghi chú</th></tr></thead><tbody>'+rows+'</tbody></table><div style="margin-top:60px;display:flex;justify-content:space-around;font-size:12px"><div style="text-align:center"><b>Bên giao</b><br>(Ký, họ tên)<div style="margin-top:30px">_______________</div></div><div style="text-align:center"><b>Bên nhận</b><br>(Ký, họ tên)<div style="margin-top:30px">_______________</div></div></div>';}
|
| 46 |
|
| 47 |
-
function doOverride(){if(typeof getQuoteData!=='function'||typeof html2canvas!=='function')return false;window._origGetQuoteData=getQuoteData;window.shareQuoteImage=async function(){var d=getData(),code=getEffectiveOrderCode(),qr=getQRUrl(d.deposit>0?d.remaining:d.grandTotal,code);try{var cv=await toCanvas(buildQuoteHTML(d,code,qr),1000);var blob=await new Promise(function(r){cv.toBlob(r,'image/png');});var f=new File([blob],code+'.png',{type:'image/png'});if(navigator.share&&navigator.canShare&&navigator.canShare({files:[f]})){await navigator.share({title:code,files:[f]});}else{var url=URL.createObjectURL(blob);var p=document.createElement('div');p.style.cssText='position:fixed;inset:0;background:rgba(0,0,0,.7);z-index:999';p.innerHTML='<div style="background:#fff;border-radius:16px;max-width:90vw;max-height:85vh"><img src="'+url+'" style="max-width:100%"><a href="'+url+'" download="'+code+'.png" style="padding:10px 20px;background:#003f62;color:#fff;border-radius:8px">⬇ Tải</a></div>';document.body.appendChild(p);}}catch(e){console.error(e);}};window.exportPDF=async function(){var d=getData(),code=getEffectiveOrderCode(),qr=getQRUrl(d.deposit>0?d.remaining:d.grandTotal,code);try{await _doExportPDF(d,code);}catch(e){console.error(e);alert('Không xuất được PDF: '+(e&&e.message?e.message:e));}};window.exportDeliveryExcel=async function(order,opt){if(typeof ExcelJS==='undefined')return;var qd=order&&order.items?orderToVAIData(order).qd:window._origGetQuoteData();var code=order&&order.code?order.code:getEffectiveOrderCode();var activeBtn=document.activeElement;if(activeBtn&&activeBtn.dataset.vaiExporting==='1')return;if(activeBtn)setExportButtonState(activeBtn,!0);try{await _doExportDeliveryExcel(qd,code);}finally{if(activeBtn)setExportButtonState(activeBtn,!1);}};window.exportDeliveryPDF=async function(order,opt){var qd=order&&order.items?orderToVAIData(order).qd:window._origGetQuoteData();var code=order&&order.code?order.code:getEffectiveOrderCode();var activeBtn=document.activeElement;if(activeBtn&&activeBtn.dataset.vaiExporting==='1')return;if(activeBtn)setExportButtonState(activeBtn,!0);try{await _doExportDeliveryPDF(qd,code);}finally{if(activeBtn)setExportButtonState(activeBtn,!1);}};window.exportExcel=async function(){if(typeof ExcelJS==='undefined')return;var d=getData(),qd=d.qd,code=getEffectiveOrderCode(),qr=getQRUrl(d.deposit>0?d.remaining:d.grandTotal,code);await _doExportExcel(d,qd,code,qr);};window.getQuoteData=function(){var qd=window._origGetQuoteData.apply(this,arguments);if(qd.customer)qd.customer.orderCode=getEffectiveOrderCode();return qd;};console.log('✅ QR Payment v1039 — Multi-download Excel
|
| 48 |
|
| 49 |
async function _doExportPDF(d,code){var qr=getQRUrl(d.deposit>0?d.remaining:d.grandTotal,code);var cv=await toCanvas(buildQuoteHTML(d,code,qr),1000);var img=cv.toDataURL('image/jpeg',0.92);var pageW=595,pageH=842,imgW=pageW,imgH=cv.height*imgW/cv.width;var pdf=new jspdf.jsPDF({orientation:'p',unit:'pt',format:'a4'});if(imgH<=pageH){pdf.addImage(img,'JPEG',0,0,imgW,imgH);}else{var y=0,rem=imgH,page=0;while(rem>0){if(page>0)pdf.addPage();pdf.addImage(img,'JPEG',0,-y,imgW,imgH);y+=pageH;rem-=pageH;page++;}}pdf.save(code+'.pdf');}
|
| 50 |
|
| 51 |
async function _doExportDeliveryPDF(qd,code){var cv=await toCanvas(buildDeliveryHTML({qd:qd},code),700);var img=cv.toDataURL('image/png');var pdf=new jspdf.jsPDF({orientation:'p',unit:'pt',format:'a4'});var margin=25,usableW=595-margin*2,ratio=cv.height/cv.width,imgH=usableW*ratio;if(imgH>842-margin*2){imgH=842-margin*2;usableW=imgH/ratio;}pdf.addImage(img,'PNG',margin,margin,usableW,imgH);pdf.save('GH-'+code+'.pdf');}
|
| 52 |
|
| 53 |
async function _doExportExcel(d,qd,code,qrUrl){
|
| 54 |
-
var MONEY_FMT='#,##0"đ"';var NUM_FMT='#,##0';var WHITE={argb:'FFFFFFFF'};
|
| 55 |
var wb=new ExcelJS.Workbook(),ws=wb.addWorksheet('Báo giá');
|
| 56 |
ws.views=[{showGridLines:false}];ws.columns=[{width:5},{width:11},{width:28},{width:13},{width:20},{width:6},{width:13},{width:13},{width:15},{width:14}];
|
| 57 |
ws.mergeCells('A3:J3');ws.getCell('A3').value='BẢNG BÁO GIÁ';ws.getCell('A3').font={bold:true,size:18,color:{argb:'FFDB9815'}};ws.getCell('A3').alignment={horizontal:'center'};
|
|
@@ -61,12 +98,11 @@ async function _doExportExcel(d,qd,code,qrUrl){
|
|
| 61 |
ws.getCell('H6').value='Ngày:';ws.mergeCells('I6:J6');ws.getCell('I6').value=qd.customer?qd.customer.date:'';
|
| 62 |
ws.getCell('A7').value='Email:';ws.mergeCells('B7:E7');ws.getCell('B7').value=qd.customer?qd.customer.email:'';
|
| 63 |
ws.getCell('A8').value='Địa chỉ:';ws.mergeCells('B8:J8');ws.getCell('B8').value=qd.customer?qd.customer.addr:'';
|
| 64 |
-
var hr=ws.getRow(10);['STT','Hình','Tên sản phẩm','Mã SP','Thông tin','SL','Đơn giá','Giá CK','Thành tiền','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'}
|
| 65 |
var cr=11,firstItemRow=cr,ckPct=Number((d.discountPercent||0));
|
| 66 |
for(var i=0;i<qd.items.length;i++){
|
| 67 |
var it=qd.items[i],row=ws.getRow(cr);row.height=50;
|
| 68 |
var bgColor=i%2===0?'FFF8FAFC':'FFFFFFFF';
|
| 69 |
-
var RB={top:{style:'thin',color:{argb:bgColor}},bottom:{style:'thin',color:{argb:bgColor}},left:{style:'thin',color:{argb:bgColor}},right:{style:'thin',color:{argb:bgColor}}};
|
| 70 |
row.getCell(1).value=it.stt||i+1;row.getCell(3).value=it.name||'';
|
| 71 |
row.getCell(3).font={bold:true,size:9};row.getCell(4).value=it.model||'';
|
| 72 |
row.getCell(5).value=it.specs||'';row.getCell(5).font={size:8,color:{argb:'FF64748B'}};
|
|
@@ -75,35 +111,26 @@ async function _doExportExcel(d,qd,code,qrUrl){
|
|
| 75 |
row.getCell(8).value=Number(it.discPrice||it.price||0);row.getCell(8).numFmt=MONEY_FMT;
|
| 76 |
if((it.discPrice||0)&&(it.price||0)&&it.discPrice<it.price)row.getCell(8).font={bold:true,color:{argb:'FFDC3545'}};
|
| 77 |
row.getCell(9).value=Number(it.total||0);row.getCell(9).numFmt=MONEY_FMT;row.getCell(9).font={bold:true};
|
| 78 |
-
row.getCell(10).value=it.note||'';
|
| 79 |
-
for(var ci=1;ci<=10;ci++){row.getCell(ci).fill={type:'pattern',pattern:'solid',fgColor:{argb:bgColor}};row.getCell(ci).border=RB;}
|
| 80 |
cr++;
|
| 81 |
}
|
| 82 |
ws.getRow(cr).height=28;
|
| 83 |
-
ws.mergeCells(cr,1,cr,5);ws.getCell('A'+cr).value='TỔNG CỘNG';ws.getCell('A'+cr).font={bold:true,size:13,color:{argb:'FFFFFFFF'}};ws.getCell('A'+cr).fill={type:'pattern',pattern:'solid',fgColor:{argb:'FF003F62'}};
|
| 84 |
-
ws.getCell('E'+cr).value={formula:'SUM(I'+firstItemRow+':I'+(cr-1)+')',result:Number(d.grandTotal||0)};ws.getCell('E'+cr).numFmt=MONEY_FMT;ws.getCell('E'+cr).font={bold:true,size:14,color:{argb:'FFF0B840'}};
|
| 85 |
wb.calcProperties.fullCalcOnLoad=true;
|
| 86 |
var buf=await wb.xlsx.writeBuffer();
|
| 87 |
var blob=new Blob([buf],{type:'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});
|
| 88 |
-
|
| 89 |
-
var a=document.createElement('a');
|
| 90 |
-
a.href=url;a.download=code+'.xlsx';
|
| 91 |
-
document.body.appendChild(a);a.click();
|
| 92 |
-
document.body.removeChild(a);
|
| 93 |
-
// FIX v1039: KHÔNG REVOKE URL - để blob tự giải phóng khi unload
|
| 94 |
}
|
| 95 |
|
| 96 |
async function _doExportDeliveryExcel(qd,code){
|
| 97 |
-
var WHITE={argb:'FFFFFFFF'};var WB={top:{style:'thin',color:WHITE},bottom:{style:'thin',color:WHITE},left:{style:'thin',color:WHITE},right:{style:'thin',color:WHITE}};
|
| 98 |
var wb=new ExcelJS.Workbook(),ws=wb.addWorksheet('Giao hàng');
|
| 99 |
ws.views=[{showGridLines:false}];ws.columns=[{width:5},{width:34},{width:14},{width:8},{width:20}];
|
| 100 |
-
ws.mergeCells('A1:E1');ws.getCell('A1').value='PHIẾU GIAO HÀNG';ws.getCell('A1').font={bold:true,size:16,color:{argb:'FF003F62'}
|
| 101 |
ws.getCell('A2').value='KH: '+(qd.customer?qd.customer.name:'');
|
| 102 |
ws.getCell('D2').value='Mã: '+code;
|
| 103 |
ws.getCell('A3').value='SĐT: '+(qd.customer?qd.customer.phone:'');
|
| 104 |
-
ws.getCell('D3').value='Ngày: '+(qd.customer?qd.customer.date||'':'');
|
| 105 |
ws.getCell('A4').value='Địa chỉ: '+(qd.customer?qd.customer.addr||'':'');
|
| 106 |
-
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'}};});
|
| 107 |
var cr=7,items=qd.items||[],totalQty=0;
|
| 108 |
for(var i=0;i<items.length;i++){
|
| 109 |
var it=items[i],row=ws.getRow(cr);row.height=20;
|
|
@@ -115,12 +142,7 @@ async function _doExportDeliveryExcel(qd,code){
|
|
| 115 |
var totalQtyRow=cr;ws.mergeCells(totalQtyRow,1,totalQtyRow,3);ws.getCell('A'+totalQtyRow).value='TỔNG CỘNG SỐ LƯỢNG';ws.getCell('D'+totalQtyRow).value=totalQty;
|
| 116 |
var buf=await wb.xlsx.writeBuffer();
|
| 117 |
var blob=new Blob([buf],{type:'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});
|
| 118 |
-
|
| 119 |
-
var a=document.createElement('a');
|
| 120 |
-
a.href=url;a.download='GH-'+code+'.xlsx';
|
| 121 |
-
document.body.appendChild(a);a.click();
|
| 122 |
-
document.body.removeChild(a);
|
| 123 |
-
// FIX v1039: KHÔNG REVOKE URL - để blob tự giải phóng khi unload
|
| 124 |
}
|
| 125 |
|
| 126 |
var att=0,t=setInterval(function(){if(doOverride()||att>120)clearInterval(t);att++;},500);
|
|
@@ -128,13 +150,43 @@ var att=0,t=setInterval(function(){if(doOverride()||att>120)clearInterval(t);att
|
|
| 128 |
document.addEventListener('click',function(e){
|
| 129 |
var btn=e.target.closest&&e.target.closest('button,a');if(!btn)return;
|
| 130 |
var txt=(btn.textContent||'').toLowerCase();if(!window._origGetQuoteData)return;
|
| 131 |
-
if(txt.includes('excel')&&txt.includes('xuất')){
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 136 |
},!0);
|
| 137 |
|
| 138 |
window.VAI_QR={getEffectiveOrderCode:getEffectiveOrderCode,getQRUrl:getQRUrl,generateOrderCode:generateOrderCode,parsePrompt:parsePrompt,getPromptText:getPromptText,injectToModal:injectToModal,getData:getData,buildQuoteHTML:buildQuoteHTML,buildDeliveryHTML:buildDeliveryHTML,toCanvas:toCanvas,exportPDF:_doExportPDF,exportExcel:_doExportExcel,exportDeliveryPDF:_doExportDeliveryPDF,exportDeliveryExcel:_doExportDeliveryExcel,fmt:fmt,compactKey:compactKey,matchItemDiscount:matchItemDiscount,parseItemDiscountLine:parseItemDiscountLine,applyPromptDiscounts:applyPromptDiscounts,setExportButtonState:setExportButtonState,orderToVAIData:orderToVAIData};
|
| 139 |
-
console.log('[VAI QR v1039] Loaded - Multi-download Excel
|
| 140 |
})();
|
|
|
|
| 1 |
+
/* V.AI STUDIO — QR Payment / Quote Data v1039 - FIXED
|
| 2 |
+
* FIX TRIỆT ĐỐI: Multi-download Excel - added blob registry
|
|
|
|
| 3 |
*/
|
| 4 |
+
(function(){'use strict';
|
| 5 |
+
|
| 6 |
+
// ===== BLOB REGISTRY - QUAN TRỌNG =====
|
| 7 |
+
window.__VAI_BLOB_REG = window.__VAI_BLOB_REG || { blobs: [], isUnloading: false };
|
| 8 |
+
var blobRegistry = window.__VAI_BLOB_REG.blobs;
|
| 9 |
+
var isUnloading = window.__VAI_BLOB_REG.isUnloading;
|
| 10 |
|
| 11 |
// Cleanup khi tab đóng
|
| 12 |
+
function cleanupBlobs(){
|
| 13 |
+
window.__VAI_BLOB_REG.isUnloading = true;
|
| 14 |
+
blobRegistry.forEach(function(e) {
|
| 15 |
+
if (!e.revoked) { try { URL.revokeObjectURL(e.url); e.revoked = true; } catch(err) {} }
|
| 16 |
+
});
|
| 17 |
+
blobRegistry = [];
|
| 18 |
+
}
|
| 19 |
+
window.addEventListener('pagehide', cleanupBlobs);
|
| 20 |
+
window.addEventListener('beforeunload', cleanupBlobs);
|
| 21 |
+
window.addEventListener('unload', cleanupBlobs);
|
| 22 |
+
|
| 23 |
+
// Patch URL.revokeObjectURL
|
| 24 |
+
if (!window.__VAI_REVOKE_PATCHED_QR) {
|
| 25 |
+
var _origRevoke = URL.revokeObjectURL;
|
| 26 |
+
URL.revokeObjectURL = function(url) {
|
| 27 |
+
if (window.__VAI_BLOB_REG.isUnloading) return _origRevoke.call(this, url);
|
| 28 |
+
if (!url || typeof url !== 'string' || !url.startsWith('blob:')) {
|
| 29 |
+
return _origRevoke.call(this, url);
|
| 30 |
+
}
|
| 31 |
+
var exists = blobRegistry.find(function(e){ return e.url === url; });
|
| 32 |
+
if (!exists) {
|
| 33 |
+
blobRegistry.push({ url: url, revoked: false, created: Date.now() });
|
| 34 |
+
}
|
| 35 |
+
return;
|
| 36 |
+
};
|
| 37 |
+
window.__VAI_REVOKE_PATCHED_QR = true;
|
| 38 |
+
}
|
| 39 |
|
| 40 |
+
// Download function với registry
|
| 41 |
+
function dlBlob(blob, filename) {
|
| 42 |
+
var url = URL.createObjectURL(blob);
|
| 43 |
+
blobRegistry.push({ url: url, revoked: false });
|
| 44 |
+
var a = document.createElement('a');
|
| 45 |
+
a.href = url;
|
| 46 |
+
a.download = filename;
|
| 47 |
+
a.style.cssText = 'position:absolute;left:-9999px';
|
| 48 |
+
document.body.appendChild(a);
|
| 49 |
+
a.click();
|
| 50 |
+
setTimeout(function() { try { document.body.removeChild(a); } catch(err) {} }, 100);
|
| 51 |
+
}
|
| 52 |
|
| 53 |
var BANK_ID='vib',BANK_ACCOUNT='918258385',BANK_NAME='TRAN QUOC VUONG',BANK_NAME_VN='Trần Quốc Vương';
|
| 54 |
function stripDiacritics(s){return s.normalize('NFD').replace(/[\u0300-\u036f]/g,'').replace(/[Đđ]/g,'D');}
|
|
|
|
| 81 |
|
| 82 |
function buildDeliveryHTML(qd,code){var rows=(qd.items||[]).map(function(it,i){return '<tr style="border-bottom:1px solid #e2e8f0"><td style="padding:8px 4px;text-align:center;font-size:12px">'+(it.stt||(i+1))+'</td><td style="padding:8px 4px;font-weight:600;font-size:12px">'+(it.name||'')+'</td><td style="padding:8px 4px;text-align:center;font-size:11px">'+(it.model||'')+'</td><td style="padding:8px 4px;text-align:center;font-weight:700;font-size:13px">'+(it.qty||1)+'</td><td style="padding:8px 4px;font-size:11px">'+(it.note||'')+'</td></tr>';}).join('');return '<div style="text-align:center;margin-bottom:14px"><div style="font-size:20px;font-weight:900;color:#003f62">PHIẾU GIAO HÀNG</div></div><div style="margin-bottom:12px;font-size:11px;line-height:1.6"><b>Khách hàng:</b> '+(qd.customer?qd.customer.name:'')+' | <b>SĐT:</b> '+(qd.customer?qd.customer.phone:'')+'<br><b>Địa chỉ:</b> '+(qd.customer?qd.customer.addr||'':'')+'<br><b>Mã đơn:</b> '+code+' | <b>Ngày:</b> '+(qd.customer?qd.customer.date||'':'')+'</div><table style="width:100%;border-collapse:collapse;font-size:12px"><thead><tr style="background:#003f62;color:#fff"><th style="padding:8px 4px;width:35px">STT</th><th style="padding:8px 4px;text-align:left">Tên sản phẩm</th><th style="padding:8px 4px;width:90px">Mã SP</th><th style="padding:8px 4px;width:40px">SL</th><th style="padding:8px 4px;text-align:left;width:120px">Ghi chú</th></tr></thead><tbody>'+rows+'</tbody></table><div style="margin-top:60px;display:flex;justify-content:space-around;font-size:12px"><div style="text-align:center"><b>Bên giao</b><br>(Ký, họ tên)<div style="margin-top:30px">_______________</div></div><div style="text-align:center"><b>Bên nhận</b><br>(Ký, họ tên)<div style="margin-top:30px">_______________</div></div></div>';}
|
| 83 |
|
| 84 |
+
function doOverride(){if(typeof getQuoteData!=='function'||typeof html2canvas!=='function')return false;window._origGetQuoteData=getQuoteData;window.shareQuoteImage=async function(){var d=getData(),code=getEffectiveOrderCode(),qr=getQRUrl(d.deposit>0?d.remaining:d.grandTotal,code);try{var cv=await toCanvas(buildQuoteHTML(d,code,qr),1000);var blob=await new Promise(function(r){cv.toBlob(r,'image/png');});var f=new File([blob],code+'.png',{type:'image/png'});if(navigator.share&&navigator.canShare&&navigator.canShare({files:[f]})){await navigator.share({title:code,files:[f]});}else{var url=URL.createObjectURL(blob);var p=document.createElement('div');p.style.cssText='position:fixed;inset:0;background:rgba(0,0,0,.7);z-index:999';p.innerHTML='<div style="background:#fff;border-radius:16px;max-width:90vw;max-height:85vh"><img src="'+url+'" style="max-width:100%"><a href="'+url+'" download="'+code+'.png" style="padding:10px 20px;background:#003f62;color:#fff;border-radius:8px">⬇ Tải</a></div>';document.body.appendChild(p);}}catch(e){console.error(e);}};window.exportPDF=async function(){var d=getData(),code=getEffectiveOrderCode(),qr=getQRUrl(d.deposit>0?d.remaining:d.grandTotal,code);try{await _doExportPDF(d,code);}catch(e){console.error(e);alert('Không xuất được PDF: '+(e&&e.message?e.message:e));}};window.exportDeliveryExcel=async function(order,opt){if(typeof ExcelJS==='undefined')return;var qd=order&&order.items?orderToVAIData(order).qd:window._origGetQuoteData();var code=order&&order.code?order.code:getEffectiveOrderCode();var activeBtn=document.activeElement;if(activeBtn&&activeBtn.dataset.vaiExporting==='1')return;if(activeBtn)setExportButtonState(activeBtn,!0);try{await _doExportDeliveryExcel(qd,code);}finally{if(activeBtn)setExportButtonState(activeBtn,!1);}};window.exportDeliveryPDF=async function(order,opt){var qd=order&&order.items?orderToVAIData(order).qd:window._origGetQuoteData();var code=order&&order.code?order.code:getEffectiveOrderCode();var activeBtn=document.activeElement;if(activeBtn&&activeBtn.dataset.vaiExporting==='1')return;if(activeBtn)setExportButtonState(activeBtn,!0);try{await _doExportDeliveryPDF(qd,code);}finally{if(activeBtn)setExportButtonState(activeBtn,!1);}};window.exportExcel=async function(){if(typeof ExcelJS==='undefined')return;var d=getData(),qd=d.qd,code=getEffectiveOrderCode(),qr=getQRUrl(d.deposit>0?d.remaining:d.grandTotal,code);await _doExportExcel(d,qd,code,qr);};window.getQuoteData=function(){var qd=window._origGetQuoteData.apply(this,arguments);if(qd.customer)qd.customer.orderCode=getEffectiveOrderCode();return qd;};console.log('✅ QR Payment v1039 — Multi-download Excel FIXED');return true;}
|
| 85 |
|
| 86 |
async function _doExportPDF(d,code){var qr=getQRUrl(d.deposit>0?d.remaining:d.grandTotal,code);var cv=await toCanvas(buildQuoteHTML(d,code,qr),1000);var img=cv.toDataURL('image/jpeg',0.92);var pageW=595,pageH=842,imgW=pageW,imgH=cv.height*imgW/cv.width;var pdf=new jspdf.jsPDF({orientation:'p',unit:'pt',format:'a4'});if(imgH<=pageH){pdf.addImage(img,'JPEG',0,0,imgW,imgH);}else{var y=0,rem=imgH,page=0;while(rem>0){if(page>0)pdf.addPage();pdf.addImage(img,'JPEG',0,-y,imgW,imgH);y+=pageH;rem-=pageH;page++;}}pdf.save(code+'.pdf');}
|
| 87 |
|
| 88 |
async function _doExportDeliveryPDF(qd,code){var cv=await toCanvas(buildDeliveryHTML({qd:qd},code),700);var img=cv.toDataURL('image/png');var pdf=new jspdf.jsPDF({orientation:'p',unit:'pt',format:'a4'});var margin=25,usableW=595-margin*2,ratio=cv.height/cv.width,imgH=usableW*ratio;if(imgH>842-margin*2){imgH=842-margin*2;usableW=imgH/ratio;}pdf.addImage(img,'PNG',margin,margin,usableW,imgH);pdf.save('GH-'+code+'.pdf');}
|
| 89 |
|
| 90 |
async function _doExportExcel(d,qd,code,qrUrl){
|
| 91 |
+
var MONEY_FMT='#,##0"đ"';var NUM_FMT='#,##0';var WHITE={argb:'FFFFFFFF'};
|
| 92 |
var wb=new ExcelJS.Workbook(),ws=wb.addWorksheet('Báo giá');
|
| 93 |
ws.views=[{showGridLines:false}];ws.columns=[{width:5},{width:11},{width:28},{width:13},{width:20},{width:6},{width:13},{width:13},{width:15},{width:14}];
|
| 94 |
ws.mergeCells('A3:J3');ws.getCell('A3').value='BẢNG BÁO GIÁ';ws.getCell('A3').font={bold:true,size:18,color:{argb:'FFDB9815'}};ws.getCell('A3').alignment={horizontal:'center'};
|
|
|
|
| 98 |
ws.getCell('H6').value='Ngày:';ws.mergeCells('I6:J6');ws.getCell('I6').value=qd.customer?qd.customer.date:'';
|
| 99 |
ws.getCell('A7').value='Email:';ws.mergeCells('B7:E7');ws.getCell('B7').value=qd.customer?qd.customer.email:'';
|
| 100 |
ws.getCell('A8').value='Địa chỉ:';ws.mergeCells('B8:J8');ws.getCell('B8').value=qd.customer?qd.customer.addr:'';
|
| 101 |
+
var hr=ws.getRow(10);['STT','Hình','Tên sản phẩm','Mã SP','Thông tin','SL','Đơn giá','Giá CK','Thành tiền','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'};});
|
| 102 |
var cr=11,firstItemRow=cr,ckPct=Number((d.discountPercent||0));
|
| 103 |
for(var i=0;i<qd.items.length;i++){
|
| 104 |
var it=qd.items[i],row=ws.getRow(cr);row.height=50;
|
| 105 |
var bgColor=i%2===0?'FFF8FAFC':'FFFFFFFF';
|
|
|
|
| 106 |
row.getCell(1).value=it.stt||i+1;row.getCell(3).value=it.name||'';
|
| 107 |
row.getCell(3).font={bold:true,size:9};row.getCell(4).value=it.model||'';
|
| 108 |
row.getCell(5).value=it.specs||'';row.getCell(5).font={size:8,color:{argb:'FF64748B'}};
|
|
|
|
| 111 |
row.getCell(8).value=Number(it.discPrice||it.price||0);row.getCell(8).numFmt=MONEY_FMT;
|
| 112 |
if((it.discPrice||0)&&(it.price||0)&&it.discPrice<it.price)row.getCell(8).font={bold:true,color:{argb:'FFDC3545'}};
|
| 113 |
row.getCell(9).value=Number(it.total||0);row.getCell(9).numFmt=MONEY_FMT;row.getCell(9).font={bold:true};
|
|
|
|
|
|
|
| 114 |
cr++;
|
| 115 |
}
|
| 116 |
ws.getRow(cr).height=28;
|
| 117 |
+
ws.mergeCells(cr,1,cr,5);ws.getCell('A'+cr).value='TỔNG CỘNG';ws.getCell('A'+cr).font={bold:true,size:13,color:{argb:'FFFFFFFF'}};ws.getCell('A'+cr).fill={type:'pattern',pattern:'solid',fgColor:{argb:'FF003F62'};};
|
| 118 |
+
ws.getCell('E'+cr).value={formula:'SUM(I'+firstItemRow+':I'+(cr-1)+')',result:Number(d.grandTotal||0)};ws.getCell('E'+cr).numFmt=MONEY_FMT;ws.getCell('E'+cr).font={bold:true,size:14,color:{argb:'FFF0B840'};};
|
| 119 |
wb.calcProperties.fullCalcOnLoad=true;
|
| 120 |
var buf=await wb.xlsx.writeBuffer();
|
| 121 |
var blob=new Blob([buf],{type:'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});
|
| 122 |
+
dlBlob(blob,code+'.xlsx');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
}
|
| 124 |
|
| 125 |
async function _doExportDeliveryExcel(qd,code){
|
|
|
|
| 126 |
var wb=new ExcelJS.Workbook(),ws=wb.addWorksheet('Giao hàng');
|
| 127 |
ws.views=[{showGridLines:false}];ws.columns=[{width:5},{width:34},{width:14},{width:8},{width:20}];
|
| 128 |
+
ws.mergeCells('A1:E1');ws.getCell('A1').value='PHIẾU GIAO HÀNG';ws.getCell('A1').font={bold:true,size:16,color:{argb:'FF003F62'};};
|
| 129 |
ws.getCell('A2').value='KH: '+(qd.customer?qd.customer.name:'');
|
| 130 |
ws.getCell('D2').value='Mã: '+code;
|
| 131 |
ws.getCell('A3').value='SĐT: '+(qd.customer?qd.customer.phone:'');
|
|
|
|
| 132 |
ws.getCell('A4').value='Địa chỉ: '+(qd.customer?qd.customer.addr||'':'');
|
| 133 |
+
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'};};});
|
| 134 |
var cr=7,items=qd.items||[],totalQty=0;
|
| 135 |
for(var i=0;i<items.length;i++){
|
| 136 |
var it=items[i],row=ws.getRow(cr);row.height=20;
|
|
|
|
| 142 |
var totalQtyRow=cr;ws.mergeCells(totalQtyRow,1,totalQtyRow,3);ws.getCell('A'+totalQtyRow).value='TỔNG CỘNG SỐ LƯỢNG';ws.getCell('D'+totalQtyRow).value=totalQty;
|
| 143 |
var buf=await wb.xlsx.writeBuffer();
|
| 144 |
var blob=new Blob([buf],{type:'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});
|
| 145 |
+
dlBlob(blob,'GH-'+code+'.xlsx');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 146 |
}
|
| 147 |
|
| 148 |
var att=0,t=setInterval(function(){if(doOverride()||att>120)clearInterval(t);att++;},500);
|
|
|
|
| 150 |
document.addEventListener('click',function(e){
|
| 151 |
var btn=e.target.closest&&e.target.closest('button,a');if(!btn)return;
|
| 152 |
var txt=(btn.textContent||'').toLowerCase();if(!window._origGetQuoteData)return;
|
| 153 |
+
if(txt.includes('excel')&&txt.includes('xuất')){
|
| 154 |
+
e.preventDefault();e.stopPropagation();
|
| 155 |
+
if(btn.dataset.vaiExporting==='1')return;
|
| 156 |
+
setExportButtonState(btn,!0);
|
| 157 |
+
window.exportExcel().finally(function(){setExportButtonState(btn,!1);});
|
| 158 |
+
return!1;
|
| 159 |
+
}
|
| 160 |
+
if((txt.includes('pdf')&&txt.includes('xuất'))||(txt.includes('pdf')&&!txt.includes('giao hàng'))){
|
| 161 |
+
e.preventDefault();e.stopPropagation();
|
| 162 |
+
if(btn.dataset.vaiExporting==='1')return;
|
| 163 |
+
setExportButtonState(btn,!0);
|
| 164 |
+
window.exportPDF().finally(function(){setExportButtonState(btn,!1);});
|
| 165 |
+
return!1;
|
| 166 |
+
}
|
| 167 |
+
if(txt.includes('chia sẻ')&&txt.includes('ảnh')){
|
| 168 |
+
e.preventDefault();e.stopPropagation();
|
| 169 |
+
if(btn.dataset.vaiExporting==='1')return;
|
| 170 |
+
setExportButtonState(btn,!0);
|
| 171 |
+
window.shareQuoteImage().finally(function(){setExportButtonState(btn,!1);});
|
| 172 |
+
return!1;
|
| 173 |
+
}
|
| 174 |
+
if((txt.includes('gh')||txt.includes('giao hàng'))&&txt.includes('excel')){
|
| 175 |
+
e.preventDefault();e.stopPropagation();
|
| 176 |
+
if(btn.dataset.vaiExporting==='1')return;
|
| 177 |
+
setExportButtonState(btn,!0);
|
| 178 |
+
window.exportDeliveryExcel().finally(function(){setExportButtonState(btn,!1);});
|
| 179 |
+
return!1;
|
| 180 |
+
}
|
| 181 |
+
if((txt.includes('gh')||txt.includes('giao hàng'))&&txt.includes('pdf')){
|
| 182 |
+
e.preventDefault();e.stopPropagation();
|
| 183 |
+
if(btn.dataset.vaiExporting==='1')return;
|
| 184 |
+
setExportButtonState(btn,!0);
|
| 185 |
+
window.exportDeliveryPDF().finally(function(){setExportButtonState(btn,!1);});
|
| 186 |
+
return!1;
|
| 187 |
+
}
|
| 188 |
},!0);
|
| 189 |
|
| 190 |
window.VAI_QR={getEffectiveOrderCode:getEffectiveOrderCode,getQRUrl:getQRUrl,generateOrderCode:generateOrderCode,parsePrompt:parsePrompt,getPromptText:getPromptText,injectToModal:injectToModal,getData:getData,buildQuoteHTML:buildQuoteHTML,buildDeliveryHTML:buildDeliveryHTML,toCanvas:toCanvas,exportPDF:_doExportPDF,exportExcel:_doExportExcel,exportDeliveryPDF:_doExportDeliveryPDF,exportDeliveryExcel:_doExportDeliveryExcel,fmt:fmt,compactKey:compactKey,matchItemDiscount:matchItemDiscount,parseItemDiscountLine:parseItemDiscountLine,applyPromptDiscounts:applyPromptDiscounts,setExportButtonState:setExportButtonState,orderToVAIData:orderToVAIData};
|
| 191 |
+
console.log('[VAI QR v1039 FIXED] Loaded - Multi-download Excel with Registry');
|
| 192 |
})();
|