Universal click fix - bắt mọi element click

#17
by bep40 - opened
Files changed (1) hide show
  1. index.html +110 -102
index.html CHANGED
@@ -1,21 +1,17 @@
1
  <!DOCTYPE html>
2
  <html lang="vi">
3
- <head><script>window.huggingface={variables:{"SPACE_CREATOR_USER_ID":"661b9191e7b0ab12bceb66f3","VAISTUDIO":"HF_TOKEN_REDACTED","REBUILD_TRIGGER":"NOW"}};</script>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width,initial-scale=1">
6
  <title>V.AI STUDIO | Niềm tin khách hàng là tài sản của chúng tôi</title>
7
- <meta name="description" content="V.AI STUDIO — 8000+ sản phẩm thiết bị nhà bếp & điện máy. Malloca, Eurogold, Grob, Canzy, Demax & Điện Máy Xanh chính hãng.">
8
  <meta property="og:type" content="website">
9
  <meta property="og:site_name" content="V.AI STUDIO">
10
- <meta property="og:title" id="ogTitle" content="V.AI STUDIO | Niềm tin khách hàng là tài sản của chúng tôi">
11
- <meta property="og:description" id="ogDesc" content="8000+ sản phẩm thiết bị nhà bếp & điện máy chính hãng. Malloca, Eurogold, Grob, Canzy, Demax & Điện Máy Xanh.">
12
  <meta property="og:image" id="ogImage" content="https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/logo_main.png">
13
  <link rel="canonical" id="canonicalUrl" href="https://bep40-v-aistudio.static.hf.space/">
14
- <meta property="og:url" id="ogUrl" content="https://bep40-v-aistudio.static.hf.space/">
15
  <meta name="twitter:card" content="summary_large_image">
16
- <meta name="twitter:title" id="twTitle" content="V.AI STUDIO">
17
- <meta name="twitter:description" id="twDesc" content="8000+ sản phẩm thiết bị nhà bếp & điện máy chính hãng">
18
- <meta name="twitter:image" id="twImage" content="https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/logo_main.png">
19
  <link rel="icon" href="https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/favicon.png">
20
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap" rel="stylesheet">
21
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
@@ -23,44 +19,49 @@
23
  *{margin:0;padding:0;box-sizing:border-box}
24
  :root{--p:#003f62;--pl:#005a8c;--a:#db9815;--al:#f0b840;--d:#0f172a;--l:#f8fafc;--g:#64748b;--gl:#e2e8f0;--w:#fff;--r:14px;--t:all .25s ease}
25
  body{font-family:'Inter',system-ui,sans-serif;color:var(--d);background:var(--l);overflow-x:hidden}
26
- ::-webkit-scrollbar{width:6px}::-webkit-scrollbar-thumb{background:var(--p);border-radius:3px}
27
- img{max-width:100%;display:block}a{text-decoration:none;color:inherit}
28
  .container{max-width:1360px;margin:0 auto;padding:0 20px}
29
  </style>
30
  </head>
31
  <body>
32
- <!-- ExcelJS Library -->
33
  <script src="https://cdn.jsdelivr.net/npm/exceljs@4.4.0/dist/exceljs.min.js"></script>
34
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
35
  <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
36
 
37
- <!-- CLICK HANDLER NGAY - TRƯỚC KHI DOM LOAD -->
38
  <script>
39
  (function(){
40
  "use strict";
41
 
42
- // Registry blob toàn cục
43
- window.__VAI_BLOB_REG = { blobs: [], isUnloading: false };
44
- var REG = window.__VAI_BLOB_REG;
45
 
46
- // Cleanup khi unload
47
- window.addEventListener('pagehide', function() {
48
- REG.isUnloading = true;
49
- REG.blobs.forEach(function(e) { try { URL.revokeObjectURL(e.url); } catch(r) {} });
50
- });
51
 
52
- // PATCH URL.revokeObjectURL NGAY LẬP TỨC
53
- (function(){var o=URL.revokeObjectURL;URL.revokeObjectURL=function(u){if(REG.isUnloading)return o.call(this,u);if(u&&typeof u==='string'&&u.startsWith('blob:')){var e=REG.blobs.find(function(x){return x.url===u});if(!e)REG.blobs.push({url:u});return;}return o.call(this,u);};})();
 
 
 
 
 
 
 
 
 
 
 
54
 
55
- // Helper
56
  function dl(blob, name) {
57
  var url = URL.createObjectURL(blob);
58
  REG.blobs.push({url:url});
59
  var a = document.createElement('a');
60
- a.href = url; a.download = name; a.style.cssText = 'position:absolute;left:-9999px';
61
  document.body.appendChild(a);
62
  a.click();
63
- setTimeout(function() { try { document.body.removeChild(a); } catch(r) {} }, 100);
64
  }
65
 
66
  function fmt(n) { return n && !isNaN(n) ? Number(n).toLocaleString('vi-VN') + 'đ' : '0đ'; }
@@ -69,97 +70,98 @@ function genCode(name) {
69
  var d = new Date(), dd = String(d.getDate()).padStart(2,'0');
70
  var mm = String(d.getMonth()+1).padStart(2,'0'), yy = String(d.getFullYear()).slice(-2);
71
  var ini = '';
72
- if(name && name.trim()) {
73
  name.trim().split(/\s+/).forEach(function(w){
74
- if(w) { var c = strip(w.charAt(0)).toUpperCase(); if(/[A-Z]/.test(c)) ini += c; }
75
  });
76
  }
77
  return 'VAS' + (ini||'X') + dd + mm + yy;
78
  }
79
- function setBtn(b, exporting) {
80
- if(exporting) {
81
- b.disabled = true;
82
- b.dataset.vaiExporting = '1';
83
- b.dataset.vaiOrig = b.innerHTML;
84
- b.innerHTML = '<span style="opacity:0.7">⏳</span>';
85
- } else {
86
- b.disabled = false;
87
- b.dataset.vaiExporting = '0';
88
- b.dataset.vaiOrig && (b.innerHTML = b.dataset.vaiOrig);
89
- }
90
- }
91
- function getQD() {
92
  var d = { customer:{name:'',phone:'',email:'',addr:'',date:new Date().toLocaleDateString('vi-VN')}, items:[], grandTotal:0 };
93
  var cart = window.cart || [], prods = window.D || [];
94
  cart.forEach(function(item,i){
95
  var p = prods[item.idx] || (prods.find ? prods.find(function(x){return x.id===item.id;}) : null);
96
  var pr = item.priceNum || item.price || (p ? p.price : 0) || 0;
97
- d.items.push({ stt:i+1, name:p ? (p.name||p.n||'') : (item.name||''), model:p ? (p.sku||p.mod||'') : (item.sku||item.model||''), qty:item.qty||1, price:pr, discPrice:pr, total:(item.qty||1)*pr });
 
 
 
 
98
  });
99
  ['#quoteName','#quoteCustomerName','#checkoutName'].forEach(function(sel){
100
  var el = document.querySelector(sel);
101
- if(el && el.value && el.value.trim()) { d.customer.name = el.value.trim(); return; }
102
  });
103
- var ph = document.querySelector('#quotePhone,#checkoutPhone'); if(ph && ph.value) d.customer.phone = ph.value.trim();
104
- var ad = document.querySelector('#quoteAddr,#checkoutAddr'); if(ad && ad.value) d.customer.addr = ad.value.trim();
 
 
105
  d.grandTotal = d.items.reduce(function(s,i){return s+(i.total||0);},0);
106
  return d;
107
  }
108
 
109
- window.VAI_EXCEL_DL = async function(t) {
110
- var qd = getQD(), code = genCode(qd.customer.name || '');
111
- if(!qd.items.length || typeof ExcelJS==='undefined') {
112
- alert(!qd.items.length ? '⚠️ Không có SP' : '⚠️ Chưa có ExcelJS');
113
- return;
114
- }
115
  try {
116
  var wb = new ExcelJS.Workbook(), ws = wb.addWorksheet('Báo giá');
117
  ws.views = [{showGridLines:false}];
118
  ws.columns = [{width:5},{width:11},{width:28},{width:13},{width:20},{width:6},{width:13},{width:13},{width:15},{width:14}];
119
  ws.mergeCells('A3:J3'); ws.getCell('A3').value = 'BẢNG BÁO GIÁ';
120
  ws.getCell('A3').font = {bold:true,size:18,color:{argb:'FFDB9815'}};
121
- ws.getCell('A3').alignment = {horizontal:'center'};
122
  ws.getCell('A5').value = 'Khách hàng:'; ws.mergeCells('B5:E5'); ws.getCell('B5').value = qd.customer.name;
123
  ws.getCell('H5').value = 'Mã đơn:'; ws.mergeCells('I5:J5'); ws.getCell('I5').value = code;
124
  ws.getCell('A6').value = 'SĐT:'; ws.mergeCells('B6:E6'); ws.getCell('B6').value = qd.customer.phone;
125
  ws.getCell('A8').value = 'Địa chỉ:'; ws.mergeCells('B8:J8'); ws.getCell('B8').value = qd.customer.addr;
 
126
  var hr = ws.getRow(10);
127
  ['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){
128
  var c = hr.getCell(i+1); c.value = h;
129
  c.font = {bold:true,color:{argb:'FFFFFFFF'}};
130
  c.fill = {type:'pattern',pattern:'solid',fgColor:{argb:'FF003F62'}};
131
  });
132
- var cr = 11, firstRow = cr;
 
133
  qd.items.forEach(function(it){
134
- var row = ws.getRow(cr); row.height = 50;
135
- row.getCell(1).value = it.stt;
136
- row.getCell(3).value = it.name; row.getCell(3).font = {bold:true};
137
- row.getCell(4).value = it.model;
138
- row.getCell(6).value = Number(it.qty); row.getCell(6).numFmt = '#,##0';
139
- row.getCell(7).value = Number(it.price); row.getCell(7).numFmt = '#,##0"đ"';
140
- row.getCell(8).value = Number(it.discPrice); row.getCell(8).numFmt = '#,##0"đ"';
141
- row.getCell(9).value = Number(it.total); row.getCell(9).numFmt = '#,##0"đ"';
142
  cr++;
143
  });
 
144
  ws.getRow(cr).height = 28; ws.mergeCells(cr,1,cr,5); ws.getCell('A'+cr).value = 'TỔNG CỘNG';
145
  ws.getCell('A'+cr).font = {bold:true};
146
  ws.getCell('A'+cr).fill = {type:'pattern',pattern:'solid',fgColor:{argb:'FF003F62'}};
147
- ws.getCell('E'+cr).value = {formula:'SUM(I'+firstRow+':I'+(cr-1)+')',result:qd.grandTotal};
148
  ws.getCell('E'+cr).numFmt = '#,##0"đ"';
149
  wb.calcProperties.fullCalcOnLoad = true;
 
150
  var buf = await wb.xlsx.writeBuffer();
151
  dl(new Blob([buf],{type:'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'}), code + '.xlsx');
152
- if(typeof showToast==='function') showToast('✅ Excel OK');
153
- } catch(err) { alert('❌ Lỗi: ' + err.message); }
 
 
154
  };
155
 
156
- window.VAI_GH_DL = async function() {
157
  var order = window.VAI_CURRENT_ORDER;
158
  var qd = order && order.items ? { customer:{name:order.customer||'',phone:order.phone||'',addr:order.addr||''}, items:order.items.map(function(it,i){
159
  return { stt:i+1, name:it.name||'', model:it.model||'', qty:it.qty||1, note:it.note||'' };
160
- }) } : getQD();
161
  var code = genCode(qd.customer.name || '');
162
- if(typeof ExcelJS==='undefined' || !qd.items.length) return;
 
163
  try {
164
  var wb = new ExcelJS.Workbook(), ws = wb.addWorksheet('Giao hàng');
165
  ws.columns = [{width:5},{width:34},{width:14},{width:8},{width:20}];
@@ -167,73 +169,79 @@ window.VAI_GH_DL = async function() {
167
  ws.getCell('A1').font = {bold:true,size:16};
168
  ws.getCell('A2').value = 'KH: ' + qd.customer.name;
169
  ws.getCell('D2').value = 'Mã: ' + code;
 
170
  var hr = ws.getRow(6);
171
  ['STT','Tên sản phẩm','Mã SP','SL','Ghi chú'].forEach(function(h,i){
172
  var c = hr.getCell(i+1); c.value = h;
173
  c.font = {bold:true,color:{argb:'FFFFFFFF'}};
174
  c.fill = {type:'pattern',pattern:'solid',fgColor:{argb:'FF003F62'}};
175
  });
 
176
  var cr = 7, tq = 0;
177
  qd.items.forEach(function(it){
178
- var row = ws.getRow(cr);
179
- row.getCell(1).value = it.stt;
180
- row.getCell(2).value = it.name;
181
- row.getCell(3).value = it.model;
182
- row.getCell(4).value = it.qty;
183
- row.getCell(5).value = it.note;
184
  tq += Number(it.qty); cr++;
185
  });
186
  ws.mergeCells(cr,1,cr,3); ws.getCell('A'+cr).value = 'TỔNG CỘNG SỐ LƯỢNG'; ws.getCell('D'+cr).value = tq;
 
187
  var buf = await wb.xlsx.writeBuffer();
188
  dl(new Blob([buf],{type:'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'}), 'GH-' + code + '.xlsx');
189
- } catch(err) {}
190
  };
191
 
192
- // CLICK HANDLER - GÁN NGAY LẬP TỨC
193
  (function(){
194
- function hdl(e) {
195
- var btn = e.target.closest('button, a');
196
- if(!btn) return;
197
- var txt = (btn.textContent || '').toLowerCase();
198
- var act = document.activeElement;
199
-
200
- // EXCEL BÁO GIÁ
201
- if(txt.includes('excel') && !txt.includes('giao') && !txt.includes('gh-')) {
202
- e.preventDefault();
203
- if(act && act.dataset.vaiExporting === '1') return;
204
- setBtn(btn, true);
205
- window.VAI_EXCEL_DL().then(function(){ setBtn(btn, false); });
206
- return false;
207
- }
208
-
209
- // GH EXCEL
210
- if((txt.includes('giao hàng') || txt.includes('gh')) && txt.includes('excel')) {
211
- e.preventDefault();
212
- if(act && act.dataset.vaiExporting === '1') return;
213
- setBtn(btn, true);
214
- window.VAI_GH_DL().then(function(){ setBtn(btn, false); });
215
- return false;
 
 
 
 
 
216
  }
217
  }
218
 
219
- // GÁN NGAY
220
- document.addEventListener('click', hdl, true);
221
- console.log('[VAI EXCEL] CLICK HANDLER ACTIVE');
222
  })();
223
 
224
  })();
225
  </script>
226
 
227
- <!-- TOPBAR -->
228
  <div class="topbar"><div class="container">
229
  <div class="topbar-item"><i class="fas fa-truck-fast"></i> Miễn phí vận chuyển</div>
230
  <div class="topbar-item"><i class="fas fa-shield-halved"></i> Bảo hành chính hãng</div>
231
  <div class="topbar-item"><i class="fas fa-headset"></i> Hỗ trợ 24/7</div>
232
  </div></div>
233
 
234
- <!-- APP -->
235
  <div id="app"></div>
236
 
237
- <script>console.log('[V.AI STUDIO] Loaded');</script>
238
  </body>
239
  </html>
 
1
  <!DOCTYPE html>
2
  <html lang="vi">
3
+ <head><script>window.huggingface={variables:{"SPACE_CREATOR_USER_ID":"661b9191e7b0ab12bceb66f3","VAISTUDIO":"HF_TOKEN_REDACTED","REBUILD_TRIGGER":"FINAL-2025"}};</script>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width,initial-scale=1">
6
  <title>V.AI STUDIO | Niềm tin khách hàng là tài sản của chúng tôi</title>
7
+ <meta name="description" content="V.AI STUDIO — 8000+ sản phẩm thiết bị nhà bếp & điện máy.">
8
  <meta property="og:type" content="website">
9
  <meta property="og:site_name" content="V.AI STUDIO">
10
+ <meta property="og:title" id="ogTitle" content="V.AI STUDIO">
11
+ <meta property="og:description" id="ogDesc" content="8000+ sản phẩm thiết bị nhà bếp & điện máy chính hãng.">
12
  <meta property="og:image" id="ogImage" content="https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/logo_main.png">
13
  <link rel="canonical" id="canonicalUrl" href="https://bep40-v-aistudio.static.hf.space/">
 
14
  <meta name="twitter:card" content="summary_large_image">
 
 
 
15
  <link rel="icon" href="https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/favicon.png">
16
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap" rel="stylesheet">
17
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
 
19
  *{margin:0;padding:0;box-sizing:border-box}
20
  :root{--p:#003f62;--pl:#005a8c;--a:#db9815;--al:#f0b840;--d:#0f172a;--l:#f8fafc;--g:#64748b;--gl:#e2e8f0;--w:#fff;--r:14px;--t:all .25s ease}
21
  body{font-family:'Inter',system-ui,sans-serif;color:var(--d);background:var(--l);overflow-x:hidden}
 
 
22
  .container{max-width:1360px;margin:0 auto;padding:0 20px}
23
  </style>
24
  </head>
25
  <body>
 
26
  <script src="https://cdn.jsdelivr.net/npm/exceljs@4.4.0/dist/exceljs.min.js"></script>
27
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
28
  <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
29
 
 
30
  <script>
31
  (function(){
32
  "use strict";
33
 
34
+ // ===== BLOB REGISTRY NGAY ======
35
+ window.__VAI_EXCEL_BLOB = { blobs: [], isUnload: false };
36
+ var REG = window.__VAI_EXCEL_BLOB;
37
 
38
+ function cleanup() { REG.isUnload = true; REG.blobs.forEach(function(e){ try{URL.revokeObjectURL(e.url);}catch(r){} }); }
39
+ window.addEventListener('pagehide', cleanup);
40
+ window.addEventListener('beforeunload', cleanup);
 
 
41
 
42
+ // PATCH URL.revokeObjectURL NGAY
43
+ if (!window._va_rev_patched) {
44
+ var _rev = URL.revokeObjectURL;
45
+ URL.revokeObjectURL = function(u) {
46
+ if (REG.isUnload) return _rev.call(this, u);
47
+ if (u && typeof u === 'string' && u.indexOf('blob:') === 0) {
48
+ if (!REG.blobs.find(function(e){return e.url===u;})) REG.blobs.push({url:u});
49
+ return;
50
+ }
51
+ return _rev.call(this, u);
52
+ };
53
+ window._va_rev_patched = true;
54
+ }
55
 
56
+ // ===== UTILITIES =====
57
  function dl(blob, name) {
58
  var url = URL.createObjectURL(blob);
59
  REG.blobs.push({url:url});
60
  var a = document.createElement('a');
61
+ a.href = url; a.download = name;
62
  document.body.appendChild(a);
63
  a.click();
64
+ setTimeout(function(){ try{document.body.removeChild(a);}catch(r){} }, 100);
65
  }
66
 
67
  function fmt(n) { return n && !isNaN(n) ? Number(n).toLocaleString('vi-VN') + 'đ' : '0đ'; }
 
70
  var d = new Date(), dd = String(d.getDate()).padStart(2,'0');
71
  var mm = String(d.getMonth()+1).padStart(2,'0'), yy = String(d.getFullYear()).slice(-2);
72
  var ini = '';
73
+ if (name && name.trim()) {
74
  name.trim().split(/\s+/).forEach(function(w){
75
+ if (w) { var c = strip(w.charAt(0)).toUpperCase(); if (/[A-Z]/.test(c)) ini += c; }
76
  });
77
  }
78
  return 'VAS' + (ini||'X') + dd + mm + yy;
79
  }
80
+
81
+ function getQuoteData() {
 
 
 
 
 
 
 
 
 
 
 
82
  var d = { customer:{name:'',phone:'',email:'',addr:'',date:new Date().toLocaleDateString('vi-VN')}, items:[], grandTotal:0 };
83
  var cart = window.cart || [], prods = window.D || [];
84
  cart.forEach(function(item,i){
85
  var p = prods[item.idx] || (prods.find ? prods.find(function(x){return x.id===item.id;}) : null);
86
  var pr = item.priceNum || item.price || (p ? p.price : 0) || 0;
87
+ d.items.push({
88
+ stt:i+1, name:p ? (p.name||p.n||'') : (item.name||''),
89
+ model:p ? (p.sku||p.mod||'') : (item.sku||item.model||''),
90
+ qty:item.qty||1, price:pr, discPrice:pr, total:(item.qty||1)*pr
91
+ });
92
  });
93
  ['#quoteName','#quoteCustomerName','#checkoutName'].forEach(function(sel){
94
  var el = document.querySelector(sel);
95
+ if (el && el.value && el.value.trim()) { d.customer.name = el.value.trim(); return; }
96
  });
97
+ var ph = document.querySelector('#quotePhone,#checkoutPhone');
98
+ if (ph && ph.value) d.customer.phone = ph.value.trim();
99
+ var ad = document.querySelector('#quoteAddr,#checkoutAddr');
100
+ if (ad && ad.value) d.customer.addr = ad.value.trim();
101
  d.grandTotal = d.items.reduce(function(s,i){return s+(i.total||0);},0);
102
  return d;
103
  }
104
 
105
+ // ===== EXPORT FUNCTIONS =====
106
+ window.VAI_DO_EXCEL = async function() {
107
+ var qd = getQuoteData(), code = genCode(qd.customer.name || '');
108
+ if (!qd.items.length) { alert('⚠️ Giỏ trống'); return; }
109
+ if (typeof ExcelJS === 'undefined') { alert('⚠️ Chưa có ExcelJS'); return; }
110
+
111
  try {
112
  var wb = new ExcelJS.Workbook(), ws = wb.addWorksheet('Báo giá');
113
  ws.views = [{showGridLines:false}];
114
  ws.columns = [{width:5},{width:11},{width:28},{width:13},{width:20},{width:6},{width:13},{width:13},{width:15},{width:14}];
115
  ws.mergeCells('A3:J3'); ws.getCell('A3').value = 'BẢNG BÁO GIÁ';
116
  ws.getCell('A3').font = {bold:true,size:18,color:{argb:'FFDB9815'}};
 
117
  ws.getCell('A5').value = 'Khách hàng:'; ws.mergeCells('B5:E5'); ws.getCell('B5').value = qd.customer.name;
118
  ws.getCell('H5').value = 'Mã đơn:'; ws.mergeCells('I5:J5'); ws.getCell('I5').value = code;
119
  ws.getCell('A6').value = 'SĐT:'; ws.mergeCells('B6:E6'); ws.getCell('B6').value = qd.customer.phone;
120
  ws.getCell('A8').value = 'Địa chỉ:'; ws.mergeCells('B8:J8'); ws.getCell('B8').value = qd.customer.addr;
121
+
122
  var hr = ws.getRow(10);
123
  ['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){
124
  var c = hr.getCell(i+1); c.value = h;
125
  c.font = {bold:true,color:{argb:'FFFFFFFF'}};
126
  c.fill = {type:'pattern',pattern:'solid',fgColor:{argb:'FF003F62'}};
127
  });
128
+
129
+ var cr = 11, first = cr;
130
  qd.items.forEach(function(it){
131
+ var r = ws.getRow(cr); r.height = 50;
132
+ r.getCell(1).value = it.stt;
133
+ r.getCell(3).value = it.name; r.getCell(3).font = {bold:true};
134
+ r.getCell(4).value = it.model;
135
+ r.getCell(6).value = Number(it.qty); r.getCell(6).numFmt = '#,##0';
136
+ r.getCell(7).value = Number(it.price); r.getCell(7).numFmt = '#,##0"đ"';
137
+ r.getCell(8).value = Number(it.discPrice); r.getCell(8).numFmt = '#,##0"đ"';
138
+ r.getCell(9).value = Number(it.total); r.getCell(9).numFmt = '#,##0"đ"';
139
  cr++;
140
  });
141
+
142
  ws.getRow(cr).height = 28; ws.mergeCells(cr,1,cr,5); ws.getCell('A'+cr).value = 'TỔNG CỘNG';
143
  ws.getCell('A'+cr).font = {bold:true};
144
  ws.getCell('A'+cr).fill = {type:'pattern',pattern:'solid',fgColor:{argb:'FF003F62'}};
145
+ ws.getCell('E'+cr).value = {formula:'SUM(I'+first+':I'+(cr-1)+')',result:qd.grandTotal};
146
  ws.getCell('E'+cr).numFmt = '#,##0"đ"';
147
  wb.calcProperties.fullCalcOnLoad = true;
148
+
149
  var buf = await wb.xlsx.writeBuffer();
150
  dl(new Blob([buf],{type:'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'}), code + '.xlsx');
151
+ if (typeof showToast === 'function') showToast('✅ Excel OK');
152
+ } catch(e) {
153
+ alert('❌ Lỗi: ' + e.message);
154
+ }
155
  };
156
 
157
+ window.VAI_DO_GH = async function() {
158
  var order = window.VAI_CURRENT_ORDER;
159
  var qd = order && order.items ? { customer:{name:order.customer||'',phone:order.phone||'',addr:order.addr||''}, items:order.items.map(function(it,i){
160
  return { stt:i+1, name:it.name||'', model:it.model||'', qty:it.qty||1, note:it.note||'' };
161
+ }) } : getQuoteData();
162
  var code = genCode(qd.customer.name || '');
163
+ if (typeof ExcelJS === 'undefined') return;
164
+
165
  try {
166
  var wb = new ExcelJS.Workbook(), ws = wb.addWorksheet('Giao hàng');
167
  ws.columns = [{width:5},{width:34},{width:14},{width:8},{width:20}];
 
169
  ws.getCell('A1').font = {bold:true,size:16};
170
  ws.getCell('A2').value = 'KH: ' + qd.customer.name;
171
  ws.getCell('D2').value = 'Mã: ' + code;
172
+
173
  var hr = ws.getRow(6);
174
  ['STT','Tên sản phẩm','Mã SP','SL','Ghi chú'].forEach(function(h,i){
175
  var c = hr.getCell(i+1); c.value = h;
176
  c.font = {bold:true,color:{argb:'FFFFFFFF'}};
177
  c.fill = {type:'pattern',pattern:'solid',fgColor:{argb:'FF003F62'}};
178
  });
179
+
180
  var cr = 7, tq = 0;
181
  qd.items.forEach(function(it){
182
+ var r = ws.getRow(cr);
183
+ r.getCell(1).value = it.stt;
184
+ r.getCell(2).value = it.name;
185
+ r.getCell(3).value = it.model;
186
+ r.getCell(4).value = it.qty;
187
+ r.getCell(5).value = it.note;
188
  tq += Number(it.qty); cr++;
189
  });
190
  ws.mergeCells(cr,1,cr,3); ws.getCell('A'+cr).value = 'TỔNG CỘNG SỐ LƯỢNG'; ws.getCell('D'+cr).value = tq;
191
+
192
  var buf = await wb.xlsx.writeBuffer();
193
  dl(new Blob([buf],{type:'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'}), 'GH-' + code + '.xlsx');
194
+ } catch(e){}
195
  };
196
 
197
+ // ===== UNIVERSAL CLICK HANDLER =====
198
  (function(){
199
+ function handleExcelClick(e) {
200
+ var el = e.target;
201
+ // Bắt mọi element - không chỉ button
202
+ while (el && el !== document) {
203
+ var txt = (el.textContent || el.innerText || '').toLowerCase();
204
+
205
+ // EXCEL BÁO GIÁ
206
+ if (txt.includes('excel') && !txt.includes('giao') && !txt.includes('gh-')) {
207
+ e.preventDefault(); e.stopImmediatePropagation();
208
+ // Set loading state
209
+ var orig = el.innerHTML;
210
+ el.innerHTML = '<span style="opacity:0.7">⏳</span>';
211
+ // Call export
212
+ window.VAI_DO_EXCEL().then(function(){ el.innerHTML = orig; });
213
+ return false;
214
+ }
215
+
216
+ // GH EXCEL
217
+ if ((txt.includes('giao hàng') || txt.includes('gh')) && txt.includes('excel')) {
218
+ e.preventDefault(); e.stopImmediatePropagation();
219
+ var orig2 = el.innerHTML;
220
+ el.innerHTML = '<span style="opacity:0.7">⏳</span>';
221
+ window.VAI_DO_GH().then(function(){ el.innerHTML = orig2; });
222
+ return false;
223
+ }
224
+
225
+ el = el.parentElement;
226
  }
227
  }
228
 
229
+ // GÁN CLICK HANDLER NGAY LẬP TỨC
230
+ document.addEventListener('click', handleExcelClick, true);
231
+ console.log('[VAI EXCEL] UNIVERSAL CLICK HANDLER ACTIVE');
232
  })();
233
 
234
  })();
235
  </script>
236
 
 
237
  <div class="topbar"><div class="container">
238
  <div class="topbar-item"><i class="fas fa-truck-fast"></i> Miễn phí vận chuyển</div>
239
  <div class="topbar-item"><i class="fas fa-shield-halved"></i> Bảo hành chính hãng</div>
240
  <div class="topbar-item"><i class="fas fa-headset"></i> Hỗ trợ 24/7</div>
241
  </div></div>
242
 
 
243
  <div id="app"></div>
244
 
245
+ <script>console.log('[V.AI STUDIO] App loading...');</script>
246
  </body>
247
  </html>