Upload qr-payment.js
Browse files- qr-payment.js +91 -96
qr-payment.js
CHANGED
|
@@ -1,15 +1,18 @@
|
|
| 1 |
/**
|
| 2 |
* V.AI STUDIO — Excel Export v1102
|
| 3 |
* FIX: Hình ảnh sản phẩm Excel 100% xuất hiện (100px x 100px)
|
| 4 |
-
* - Row height: 120 (chứa ảnh 100px + padding)
|
| 5 |
-
* - Image size: 100x100px
|
| 6 |
-
* - CORS fallback via allorigins.win
|
| 7 |
*/
|
| 8 |
(function(){
|
| 9 |
'use strict';
|
| 10 |
var BANK_ID='vib',BANK_ACCOUNT='918258385',BANK_NAME='TRAN QUOC VUONG',BANK_NAME_VN='Trần Quốc Vương';
|
| 11 |
|
| 12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
async function fetchImageAsArrayBuffer(url){
|
| 14 |
if(!url||!url.startsWith('http')) return null;
|
| 15 |
try{
|
|
@@ -23,65 +26,75 @@ async function fetchImageAsArrayBuffer(url){
|
|
| 23 |
return null;
|
| 24 |
}
|
| 25 |
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
|
|
|
|
|
|
|
|
|
| 32 |
}
|
| 33 |
|
| 34 |
-
|
| 35 |
-
document.
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
// Override exportExcel to include images
|
| 43 |
-
if(!window.VAI_QR){
|
| 44 |
-
window.VAI_QR = {};
|
| 45 |
-
}
|
| 46 |
-
|
| 47 |
-
var origExportExcel = window.VAI_QR.exportExcel;
|
| 48 |
-
if(!origExportExcel && typeof window.exportExcel !== 'function'){
|
| 49 |
-
window.VAI_QR.exportExcel = async function(d,qd,code,qrUrl,blackWhite){
|
| 50 |
-
await doExportExcelWithImages(d,qd,code,qrUrl);
|
| 51 |
-
};
|
| 52 |
}
|
|
|
|
| 53 |
}
|
| 54 |
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
alert('❌ Giỏ trống!');
|
| 59 |
return;
|
| 60 |
}
|
| 61 |
|
| 62 |
-
if(typeof ExcelJS === 'undefined'){
|
| 63 |
-
var s = document.createElement('script');
|
| 64 |
-
s.src = 'https://cdn.jsdelivr.net/npm/exceljs@4.4.0/dist/exceljs.min.js';
|
| 65 |
-
document.head.appendChild(s);
|
| 66 |
-
await new Promise(function(r){s.onload=r});
|
| 67 |
-
}
|
| 68 |
-
|
| 69 |
try{
|
| 70 |
var wb = new ExcelJS.Workbook();
|
| 71 |
var ws = wb.addWorksheet('Báo giá');
|
| 72 |
|
| 73 |
-
// Columns
|
| 74 |
ws.columns = [
|
| 75 |
-
{width:6},
|
| 76 |
-
{width:22},
|
| 77 |
-
{width:18}, {width:16}
|
| 78 |
];
|
| 79 |
|
| 80 |
// Header
|
| 81 |
ws.mergeCells('A3:J3');
|
| 82 |
ws.getCell('A3').value = 'BẢNG BÁO GIÁ';
|
| 83 |
ws.getCell('A3').font = {bold:true, size:18, color:{argb:'FFDB9815'}};
|
| 84 |
-
ws.getCell('A3').alignment = {horizontal:'center', vertical:'middle'};
|
| 85 |
ws.getRow(3).height = 30;
|
| 86 |
|
| 87 |
var headerRow = ws.getRow(10);
|
|
@@ -94,22 +107,20 @@ async function doExportExcelWithImages(d,qd,code,qrUrl){
|
|
| 94 |
});
|
| 95 |
headerRow.height = 20;
|
| 96 |
|
| 97 |
-
// Items with images
|
| 98 |
var r = 11;
|
| 99 |
-
await Promise.all(
|
| 100 |
-
var p = window.D && D[it.idx] ? D[it.idx] : null;
|
| 101 |
var row = ws.getRow(r);
|
| 102 |
-
row.height = 120; // 100px image + padding
|
| 103 |
|
| 104 |
var bgColor = i % 2 === 0 ? 'FFF8FAFC' : 'FFFFFFFF';
|
| 105 |
|
| 106 |
-
row.getCell(1).value =
|
| 107 |
row.getCell(1).alignment = {horizontal:'center', vertical:'middle'};
|
| 108 |
|
| 109 |
-
// IMAGE 100x100px
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
var imgBuf = await fetchImageAsArrayBuffer(imgUrl);
|
| 113 |
if(imgBuf){
|
| 114 |
try{
|
| 115 |
wb.addImage(wb.addImage({buffer:imgBuf, extension:'png'}),
|
|
@@ -118,18 +129,14 @@ async function doExportExcelWithImages(d,qd,code,qrUrl){
|
|
| 118 |
}
|
| 119 |
}
|
| 120 |
|
| 121 |
-
row.getCell(3).value = it.name
|
| 122 |
-
row.getCell(
|
| 123 |
-
row.getCell(
|
| 124 |
-
row.getCell(
|
| 125 |
-
row.getCell(
|
| 126 |
-
row.getCell(
|
| 127 |
-
row.getCell(
|
| 128 |
-
row.getCell(
|
| 129 |
-
row.getCell(8).numFmt = '#,##0"đ"';
|
| 130 |
-
row.getCell(9).value = Number(it.total||0);
|
| 131 |
-
row.getCell(9).numFmt = '#,##0"đ"';
|
| 132 |
-
row.getCell(10).value = it.note || '';
|
| 133 |
|
| 134 |
for(var ci=1; ci<=10; ci++){
|
| 135 |
row.getCell(ci).fill = {type:'pattern', pattern:'solid', fgColor:{argb:bgColor}};
|
|
@@ -139,18 +146,12 @@ async function doExportExcelWithImages(d,qd,code,qrUrl){
|
|
| 139 |
|
| 140 |
// Total row
|
| 141 |
var lastRow = r-1;
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
ws.
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
totalRow.getCell(6).value = {formula:'SUM(F11:F'+lastRow+')'};
|
| 149 |
-
totalRow.getCell(6).font = {bold:true, size:12, color:{argb:'FFFFFFFF'}};
|
| 150 |
-
totalRow.getCell(6).fill = {type:'pattern', pattern:'solid', fgColor:{argb:'FF003F62'}};
|
| 151 |
-
totalRow.getCell(9).value = {formula:'SUM(I11:I'+lastRow+')'};
|
| 152 |
-
totalRow.getCell(9).font = {bold:true, size:14, color:{argb:'FFF0B840'}};
|
| 153 |
-
totalRow.getCell(9).fill = {type:'pattern', pattern:'solid', fgColor:{argb:'FF003F62'}};
|
| 154 |
|
| 155 |
wb.calcProperties.fullCalcOnLoad = true;
|
| 156 |
var buf = await wb.xlsx.writeBuffer();
|
|
@@ -161,43 +162,37 @@ async function doExportExcelWithImages(d,qd,code,qrUrl){
|
|
| 161 |
var url = URL.createObjectURL(blob);
|
| 162 |
var a = document.createElement('a');
|
| 163 |
a.href = url;
|
| 164 |
-
a.download =
|
| 165 |
document.body.appendChild(a);
|
| 166 |
a.click();
|
| 167 |
-
setTimeout(function(){document.body.removeChild(a); URL.revokeObjectURL(url);}, 30000);
|
| 168 |
|
| 169 |
-
console.log('[VAI v1102] ✅ Excel
|
| 170 |
|
| 171 |
}catch(e){
|
| 172 |
console.error('[VAI v1102] Lỗi export Excel:', e);
|
| 173 |
-
alert('❌ Lỗi: ' + (e.message||e));
|
| 174 |
}
|
| 175 |
}
|
| 176 |
|
| 177 |
-
function
|
| 178 |
if(!specs) return '';
|
| 179 |
if(typeof specs === 'string') return specs.slice(0,100);
|
| 180 |
if(typeof specs === 'object'){
|
| 181 |
var parts = [];
|
| 182 |
Object.entries(specs).forEach(function(e){
|
| 183 |
-
if(e[1] != null
|
| 184 |
});
|
| 185 |
-
return parts.join('; ').slice(0,
|
| 186 |
}
|
| 187 |
return '';
|
| 188 |
}
|
| 189 |
|
| 190 |
-
//
|
| 191 |
-
(
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
} else if(typeof window.exportExcel === 'function'){
|
| 197 |
-
// Original exists, wrap it
|
| 198 |
-
}
|
| 199 |
-
if(++tries > 240) clearInterval(intr);
|
| 200 |
-
}, 500);
|
| 201 |
-
})();
|
| 202 |
|
| 203 |
-
console.log('[VAI v1102] Excel export
|
|
|
|
| 1 |
/**
|
| 2 |
* V.AI STUDIO — Excel Export v1102
|
| 3 |
* FIX: Hình ảnh sản phẩm Excel 100% xuất hiện (100px x 100px)
|
|
|
|
|
|
|
|
|
|
| 4 |
*/
|
| 5 |
(function(){
|
| 6 |
'use strict';
|
| 7 |
var BANK_ID='vib',BANK_ACCOUNT='918258385',BANK_NAME='TRAN QUOC VUONG',BANK_NAME_VN='Trần Quốc Vương';
|
| 8 |
|
| 9 |
+
function fmt(n){
|
| 10 |
+
if(n==null||n==='')return'0đ';
|
| 11 |
+
var num=typeof n==='string'?parseFloat(n.replace(/[^\d.-]/g,'')):Number(n);
|
| 12 |
+
if(isNaN(num))return'0đ';
|
| 13 |
+
return num.toLocaleString('vi-VN')+'đ';
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
async function fetchImageAsArrayBuffer(url){
|
| 17 |
if(!url||!url.startsWith('http')) return null;
|
| 18 |
try{
|
|
|
|
| 26 |
return null;
|
| 27 |
}
|
| 28 |
|
| 29 |
+
function generateOrderCode(name){
|
| 30 |
+
var now = new Date(), dd = String(now.getDate()).padStart(2,'0'),
|
| 31 |
+
mm = String(now.getMonth()+1).padStart(2,'0'), yy = String(now.getFullYear()).slice(-2), ini='';
|
| 32 |
+
if(name && name.trim()){
|
| 33 |
+
name.trim().split(/\s+/).forEach(function(w){
|
| 34 |
+
if(w){ var ch = w.charAt(0).toUpperCase(); if(/[A-Z]/.test(ch)) ini += ch; }
|
| 35 |
+
});
|
| 36 |
+
}
|
| 37 |
+
return 'VAS' + (ini || 'X') + dd + mm + yy;
|
| 38 |
}
|
| 39 |
|
| 40 |
+
function getEffectiveOrderCode(){
|
| 41 |
+
var n = document.getElementById('quoteName')?.value || '';
|
| 42 |
+
if(n && n.trim()) return generateOrderCode(n);
|
| 43 |
+
var h = window.location.hash;
|
| 44 |
+
if(h && h.length > 1){
|
| 45 |
+
var c = h.substring(1);
|
| 46 |
+
if(/^VAS[A-Z0-9]{3,}$/.test(c)) return c;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
}
|
| 48 |
+
return generateOrderCode('');
|
| 49 |
}
|
| 50 |
|
| 51 |
+
function getQRUrl(amount, code){
|
| 52 |
+
return 'https://img.vietqr.io/image/' + BANK_ID + '-' + BANK_ACCOUNT + '-compact2.png?amount=' +
|
| 53 |
+
Math.round(amount||0) + '&addInfo=' + encodeURIComponent(code||'VAISTUDIO') +
|
| 54 |
+
'&accountName=' + encodeURIComponent(BANK_NAME);
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
// Export Excel with 100x100px images
|
| 58 |
+
async function exportExcel(d, qd, code, qrUrl, blackWhite){
|
| 59 |
+
qd = qd || d?.qd;
|
| 60 |
+
code = code || getEffectiveOrderCode();
|
| 61 |
+
qrUrl = qrUrl || getQRUrl(d?.grandTotal || qd?.grandTotal || 0, code);
|
| 62 |
+
|
| 63 |
+
var items = (qd?.items || window.cart || []).map(function(it, i){
|
| 64 |
+
var p = window.D && D[it.idx] ? D[it.idx] : null;
|
| 65 |
+
return {
|
| 66 |
+
stt: i+1,
|
| 67 |
+
name: it.name || (p?p.name||p.n:'') || '',
|
| 68 |
+
model: it.model || (p?p.sku||p.model:'') || '',
|
| 69 |
+
image: p?.image || it.image || it.img || '',
|
| 70 |
+
specs: it.specs || (p && p.specs ? formatSpecs(p.specs) : '') || '',
|
| 71 |
+
qty: Number(it.qty||1),
|
| 72 |
+
price: Number(it.price||0),
|
| 73 |
+
discPrice: Number(it.discPrice||it.price||0),
|
| 74 |
+
total: Number(it.total||(it.discPrice||it.price)*(it.qty||1)||0),
|
| 75 |
+
note: it.note || ''
|
| 76 |
+
};
|
| 77 |
+
});
|
| 78 |
+
|
| 79 |
+
if(!items.length){
|
| 80 |
alert('❌ Giỏ trống!');
|
| 81 |
return;
|
| 82 |
}
|
| 83 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 84 |
try{
|
| 85 |
var wb = new ExcelJS.Workbook();
|
| 86 |
var ws = wb.addWorksheet('Báo giá');
|
| 87 |
|
| 88 |
+
// Columns with Hình = 15 width
|
| 89 |
ws.columns = [
|
| 90 |
+
{width:6},{width:15},{width:30},{width:15},
|
| 91 |
+
{width:22},{width:8},{width:15},{width:15},{width:18},{width:16}
|
|
|
|
| 92 |
];
|
| 93 |
|
| 94 |
// Header
|
| 95 |
ws.mergeCells('A3:J3');
|
| 96 |
ws.getCell('A3').value = 'BẢNG BÁO GIÁ';
|
| 97 |
ws.getCell('A3').font = {bold:true, size:18, color:{argb:'FFDB9815'}};
|
|
|
|
| 98 |
ws.getRow(3).height = 30;
|
| 99 |
|
| 100 |
var headerRow = ws.getRow(10);
|
|
|
|
| 107 |
});
|
| 108 |
headerRow.height = 20;
|
| 109 |
|
| 110 |
+
// Items with 100x100px images
|
| 111 |
var r = 11;
|
| 112 |
+
await Promise.all(items.map(async function(it, i){
|
|
|
|
| 113 |
var row = ws.getRow(r);
|
| 114 |
+
row.height = 120; // For 100px image + padding
|
| 115 |
|
| 116 |
var bgColor = i % 2 === 0 ? 'FFF8FAFC' : 'FFFFFFFF';
|
| 117 |
|
| 118 |
+
row.getCell(1).value = it.stt;
|
| 119 |
row.getCell(1).alignment = {horizontal:'center', vertical:'middle'};
|
| 120 |
|
| 121 |
+
// IMAGE 100x100px
|
| 122 |
+
if(it.image && it.image.startsWith('http')){
|
| 123 |
+
var imgBuf = await fetchImageAsArrayBuffer(it.image);
|
|
|
|
| 124 |
if(imgBuf){
|
| 125 |
try{
|
| 126 |
wb.addImage(wb.addImage({buffer:imgBuf, extension:'png'}),
|
|
|
|
| 129 |
}
|
| 130 |
}
|
| 131 |
|
| 132 |
+
row.getCell(3).value = it.name; row.getCell(3).font = {bold:true, size:9};
|
| 133 |
+
row.getCell(4).value = it.model;
|
| 134 |
+
row.getCell(5).value = it.specs;
|
| 135 |
+
row.getCell(6).value = it.qty;
|
| 136 |
+
row.getCell(7).value = it.price; row.getCell(7).numFmt = '#,##0"đ"';
|
| 137 |
+
row.getCell(8).value = it.discPrice; row.getCell(8).numFmt = '#,##0"đ"';
|
| 138 |
+
row.getCell(9).value = it.total; row.getCell(9).numFmt = '#,##0"đ"';
|
| 139 |
+
row.getCell(10).value = it.note;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 140 |
|
| 141 |
for(var ci=1; ci<=10; ci++){
|
| 142 |
row.getCell(ci).fill = {type:'pattern', pattern:'solid', fgColor:{argb:bgColor}};
|
|
|
|
| 146 |
|
| 147 |
// Total row
|
| 148 |
var lastRow = r-1;
|
| 149 |
+
ws.getCell('A'+r).value = 'TỔNG CỘNG';
|
| 150 |
+
ws.getCell('A'+r).font = {bold:true, size:13, color:{argb:'FFFFFFFF'}};
|
| 151 |
+
ws.getCell('A'+r).fill = {type:'pattern', pattern:'solid', fgColor:{argb:'FF003F62'}};
|
| 152 |
+
ws.getCell('G'+r).value = {formula:'SUM(G11:G'+lastRow+')', result: items.reduce((s,it)=>s+it.total,0)};
|
| 153 |
+
ws.getCell('G'+r).numFmt = '#,##0"đ"';
|
| 154 |
+
ws.getCell('G'+r).font = {bold:true, size:14, color:{argb:'FFF0B840'}};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 155 |
|
| 156 |
wb.calcProperties.fullCalcOnLoad = true;
|
| 157 |
var buf = await wb.xlsx.writeBuffer();
|
|
|
|
| 162 |
var url = URL.createObjectURL(blob);
|
| 163 |
var a = document.createElement('a');
|
| 164 |
a.href = url;
|
| 165 |
+
a.download = code + '.xlsx';
|
| 166 |
document.body.appendChild(a);
|
| 167 |
a.click();
|
| 168 |
+
setTimeout(function(){ document.body.removeChild(a); URL.revokeObjectURL(url); }, 30000);
|
| 169 |
|
| 170 |
+
console.log('[VAI v1102] ✅ Excel with 100x100px images');
|
| 171 |
|
| 172 |
}catch(e){
|
| 173 |
console.error('[VAI v1102] Lỗi export Excel:', e);
|
| 174 |
+
alert('❌ Lỗi: ' + (e.message || e));
|
| 175 |
}
|
| 176 |
}
|
| 177 |
|
| 178 |
+
function formatSpecs(specs){
|
| 179 |
if(!specs) return '';
|
| 180 |
if(typeof specs === 'string') return specs.slice(0,100);
|
| 181 |
if(typeof specs === 'object'){
|
| 182 |
var parts = [];
|
| 183 |
Object.entries(specs).forEach(function(e){
|
| 184 |
+
if(e[1] != null) parts.push(e[0] + ': ' + e[1]);
|
| 185 |
});
|
| 186 |
+
return parts.join('; ').slice(0,100);
|
| 187 |
}
|
| 188 |
return '';
|
| 189 |
}
|
| 190 |
|
| 191 |
+
// Export to VAI_QR
|
| 192 |
+
if(!window.VAI_QR) window.VAI_QR = {};
|
| 193 |
+
window.VAI_QR.exportExcel = exportExcel;
|
| 194 |
+
window.VAI_QR.getEffectiveOrderCode = getEffectiveOrderCode;
|
| 195 |
+
window.VAI_QR.getQRUrl = getQRUrl;
|
| 196 |
+
window.VAI_QR.fmt = fmt;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 197 |
|
| 198 |
+
console.log('[VAI v1102] Excel export ready - 100px images ✅');
|