bep40 commited on
Commit
08b4786
·
verified ·
1 Parent(s): c16319b

Upload vai-excel-ultimate-fix.js

Browse files
Files changed (1) hide show
  1. vai-excel-ultimate-fix.js +110 -142
vai-excel-ultimate-fix.js CHANGED
@@ -1,35 +1,46 @@
1
  /**
2
- * V.AI STUDIO - Excel Export Fix v8.1 FINAL
3
- * ========================================
4
- * ROOT CAUSE: Chrome's "transient activation" expires after async operations
5
- * SOLUTION: Pre-build xlsx in background, download SYNC from cache on click
 
 
6
  */
7
  (function() {
8
  'use strict';
9
 
10
- var cache = { quote: null, delivery: null };
 
11
 
12
- // SYNC download - must be called within user gesture
13
- function downloadSync(blob, filename) {
 
 
 
 
 
 
14
  var url = URL.createObjectURL(blob);
 
 
15
  var a = document.createElement('a');
16
  a.href = url;
17
  a.download = filename;
18
- a.style.cssText = 'position:fixed;top:-10000px;';
19
  document.body.appendChild(a);
 
 
20
  a.click();
 
 
21
  setTimeout(function() {
22
- document.body.removeChild(a);
23
- }, 200);
24
- }
25
-
26
- // SYNC CSV download - always works
27
- function downloadCSVSync(csv, filename) {
28
- var blob = new Blob(['\uFEFF' + csv], { type: 'text/csv;charset=utf-8;' });
29
- return downloadSync(blob, filename);
30
  }
31
 
32
- // Get data safely
33
  function getQuoteDataSafe() {
34
  try {
35
  if (typeof getQuoteData === 'function') return getQuoteData();
@@ -37,162 +48,119 @@
37
  return { customer: {}, items: [] };
38
  }
39
 
40
- function getCartSafe() {
41
- var cart = window.cart || [];
42
- try {
43
- var s = localStorage.getItem('malloca_cart');
44
- if (s) {
45
- var p = JSON.parse(s);
46
- if (Array.isArray(p) && p.length) cart = p;
47
- }
48
- } catch(e) {}
49
- return cart;
50
- }
51
-
52
- // Build CSV (simple, works always)
53
- function buildCSV(qd) {
54
- var csv = 'BÁO GIÁ V.AI STUDIO\n';
55
- csv += 'Khách hàng:,' + (qd.customer.name || '') + '\n';
56
- csv += 'SĐT:,' + (qd.customer.phone || '') + '\n';
57
- csv += 'Mã đơn:,' + (qd.customer.orderCode || '') + '\n\n';
58
- csv += 'STT,Tên,Mã SP,SL,Đơn giá,Thành tiền,Ghi chú\n';
59
- var total = 0;
60
- (qd.items || []).forEach(function(it, i) {
61
- var lt = (it.qty || 1) * (it.discPrice || it.price || 0);
62
- total += lt;
63
- csv += (i + 1) + ',' + (it.name || '').replace(/,/g, '-') + ',' + (it.model || '') + ',' + (it.qty || 1) + ',' + (it.price || 0) + ',' + lt + ',' + (it.note || '') + '\n';
64
- });
65
- csv += '\n,,,,,,TỔNG:,' + total + '\n';
66
- return csv;
67
- }
68
-
69
- function buildDeliveryCSV(cart) {
70
- var csv = 'PHIẾU GIAO HÀNG\n\n';
71
- csv += 'STT,Mã SP,Tên,SL,Đơn giá,Thành tiền\n';
72
- var total = 0;
73
- (cart || []).forEach(function(c, i) {
74
- var price = Number(c.priceNum || 0) || 0;
75
- var qty = Number(c.qty || 1);
76
- var lt = price * qty;
77
- total += lt;
78
- csv += (i + 1) + ',' + (c.sku || '') + ',' + (c.name || '').replace(/,/g, '-') + ',' + qty + ',' + price + ',' + lt + '\n';
79
- });
80
- csv += '\n,,,,,TỔNG:,' + total + '\n';
81
- return csv;
82
- }
83
-
84
- // Build xlsx (async, called in background)
85
- function buildXLSX(qd, cb) {
86
- if (typeof ExcelJS === 'undefined') { cb(null); return; }
87
- try {
88
  var wb = new ExcelJS.Workbook();
89
- var ws = wb.addWorksheet('Báo Giá');
90
  ws.columns = [
91
- { width: 6 }, { width: 32 }, { width: 16 },
92
- { width: 28 }, { width: 18 }, { width: 8 },
93
- { width: 16 }, { width: 16 }, { width: 18 }, { width: 18 }
94
  ];
95
- ws.mergeCells('A1:J1');
96
- ws.getCell('A1').value = 'BÁO GIÁ V.AI STUDIO';
97
- ws.getCell('A1').font = { bold: true, size: 16, color: { argb: 'FFDB9815' } };
98
 
99
- var r = 3;
 
 
 
 
 
100
  (qd.items || []).forEach(function(it, i) {
101
  r++;
102
  var row = ws.getRow(r);
 
 
 
 
 
 
103
  row.values = [
104
- i + 1, '', it.name || '', it.model || '', it.specs || '',
105
- it.qty || 1, it.price || 0, it.discPrice || it.price || 0,
106
- (it.qty || 1) * (it.discPrice || it.price || 0), it.note || ''
107
  ];
108
- if (i % 2 === 1) {
109
- for (var c = 1; c <= 10; c++) {
110
- row.getCell(c).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFF8FAFC' } };
111
- }
112
- }
113
  });
114
 
115
- var total = (qd.items || []).reduce(function(s, it) {
116
- return s + (it.qty || 1) * (it.discPrice || it.price || 0);
117
- }, 0);
 
 
118
 
119
- var tr = ws.getRow(++r);
120
- ws.mergeCells('A' + r + ':I' + r);
121
- tr.getCell(1).value = 'TỔNG CỘNG';
122
- tr.getCell(1).font = { bold: true, size: 12 };
123
- tr.getCell(9).value = total;
124
- tr.getCell(9).numFmt = '#,##0"đ"';
125
- tr.getCell(9).font = { bold: true, size: 12 };
126
-
127
- wb.xlsx.writeBuffer().then(cb).catch(cb);
128
- } catch(e) { cb(null); }
129
- }
130
-
131
- // Prebuild xlsx when modal opens (background, no gesture needed)
132
- function prebuildQuote() {
133
- var qd = getQuoteDataSafe();
134
- if (!qd || !qd.items || !qd.items.length) return;
135
- buildXLSX(qd, function(buf) {
136
- if (buf) {
137
- cache.quote = { buffer: buf, filename: (qd.customer.orderCode || 'BG') + '.xlsx' };
138
- if (typeof showToast === 'function') {
139
- showToast('✅ File Excel sẵn sàng! Bấm để tải');
140
- }
141
- }
142
  });
143
  }
144
 
145
- // OVERRIDE exportExcel - DOWNLOAD FIRST, TOAST AFTER
146
  window.exportExcel = function() {
147
  var qd = getQuoteDataSafe();
 
 
 
148
  if (!qd || !qd.items || !qd.items.length) {
149
  if (typeof showToast === 'function') showToast('⚠️ Không có sản phẩm');
 
150
  return;
151
  }
152
 
153
- var xlsxName = (qd.customer.orderCode || 'BG') + '.xlsx';
154
- var csvName = xlsxName.replace('.xlsx', '.csv');
155
-
156
- // CACHE READY → download xlsx SYNC (guaranteed)
157
- if (cache.quote && cache.quote.buffer) {
158
- downloadSync(cache.quote.buffer, xlsxName);
159
- if (typeof showToast === 'function') showToast('✅ Đã tải ' + xlsxName);
160
- // Rebuild cache
161
- prebuildQuote();
162
  return;
163
  }
164
 
165
- // NO CACHE → download CSV SYNC (guaranteed), build xlsx in background
166
- downloadCSVSync(buildCSV(qd), csvName);
167
- if (typeof showToast === 'function') showToast('⏳ Excel đang tạo... CSV đã tải');
168
- prebuildQuote();
 
 
 
 
 
 
169
  };
170
 
171
- // OVERRIDE exportDeliveryExcel
172
  window.exportDeliveryExcel = function() {
173
- var cart = getCartSafe();
174
- if (!cart || !cart.length) {
175
- if (typeof showToast === 'function') showToast('⚠️ Giỏ trống');
 
 
 
176
  return;
177
  }
178
 
179
- var name = 'Giao_hang.xlsx';
180
- downloadCSVSync(buildDeliveryCSV(cart), name.replace('.xlsx', '.csv'));
181
- if (typeof showToast === 'function') showToast('✅ Đã tải CSV giao hàng');
182
- };
183
-
184
- // Hook openQuotation to start prebuilding
185
- if (!window.__VAI_HOOKED_V81) {
186
- var orig = window.openQuotation;
187
- if (typeof orig === 'function') {
188
- window.openQuotation = function() {
189
- var r = orig.apply(this, arguments);
190
- setTimeout(prebuildQuote, 300);
191
- return r;
192
- };
193
  }
194
- window.__VAI_HOOKED_V81 = true;
195
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
 
197
- console.log('[VAI v8.1] Ready - SYNC download + cache');
198
  })();
 
1
  /**
2
+ * V.AI STUDIO Excel Export Fix FINAL
3
+ *
4
+ * FIX CHO SPACE CÓ DẤU CHẤM (V.AISTUDIO)
5
+ * - Tên dấu chấm gây CDN cache khó bust
6
+ * - Inline sync download, không async/await
7
+ * - Mỗi click tạo blob mới, không revoke sớm
8
  */
9
  (function() {
10
  'use strict';
11
 
12
+ // Blob URL registry - KHÔNG REVKE SỚM
13
+ var blobUrls = [];
14
 
15
+ // Cleanup khi tab đóng
16
+ window.addEventListener('beforeunload', function() {
17
+ blobUrls.forEach(function(u) { try { URL.revokeObjectURL(u); } catch(e){} });
18
+ });
19
+
20
+ // Download SYNC - phải trong user gesture
21
+ function downloadSync(data, filename, mime) {
22
+ var blob = (data instanceof Blob) ? data : new Blob([data], {type: mime || 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});
23
  var url = URL.createObjectURL(blob);
24
+ blobUrls.push(url);
25
+
26
  var a = document.createElement('a');
27
  a.href = url;
28
  a.download = filename;
29
+ a.style.cssText = 'position:fixed;top:-9999px;';
30
  document.body.appendChild(a);
31
+
32
+ // Click NGAY - không được đưa ra setTimeout/setInterval
33
  a.click();
34
+
35
+ // Cleanup sau
36
  setTimeout(function() {
37
+ try { document.body.removeChild(a); } catch(e) {}
38
+ }, 100);
39
+
40
+ return true;
 
 
 
 
41
  }
42
 
43
+ // Get quote data
44
  function getQuoteDataSafe() {
45
  try {
46
  if (typeof getQuoteData === 'function') return getQuoteData();
 
48
  return { customer: {}, items: [] };
49
  }
50
 
51
+ // Build xlsx - trả về Promise
52
+ function buildXLSX(qd) {
53
+ return new Promise(function(resolve) {
54
+ if (!qd || !qd.items || !qd.items.length) { resolve(null); return; }
55
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  var wb = new ExcelJS.Workbook();
57
+ var ws = wb.addWorksheet('Báo giá');
58
  ws.columns = [
59
+ {width:6}, {width:32}, {width:16}, {width:25},
60
+ {width:18}, {width:8}, {width:15}, {width:15}, {width:18}, {width:18}
 
61
  ];
 
 
 
62
 
63
+ // Header
64
+ ws.mergeCells('A1:J1').value = 'BÁO GIÁ V.AI STUDIO';
65
+ ws.getCell('A1').font = {bold:true, size:16};
66
+
67
+ // Dữ liệu
68
+ var r = 3, total = 0;
69
  (qd.items || []).forEach(function(it, i) {
70
  r++;
71
  var row = ws.getRow(r);
72
+ var qty = Number(it.qty || 1);
73
+ var price = Number(it.price || 0);
74
+ var discPrice = Number(it.discPrice || price);
75
+ var lineTotal = qty * discPrice;
76
+ total += lineTotal;
77
+
78
  row.values = [
79
+ i+1, it.name || '', it.model || '', it.specs || '', '',
80
+ qty, price, discPrice, lineTotal, it.note || ''
 
81
  ];
 
 
 
 
 
82
  });
83
 
84
+ // Tổng cộng
85
+ r++;
86
+ ws.mergeCells('A' + r + ':H' + r).value = 'TỔNG CỘNG';
87
+ ws.getCell('I' + r).value = total;
88
+ ws.getCell('I' + r).numFmt = '#,##0"đ"';
89
 
90
+ wb.xlsx.writeBuffer().then(resolve).catch(resolve);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  });
92
  }
93
 
94
+ // EXPORT EXCEL - FIXED FOR DẤU CHẤM SPACE
95
  window.exportExcel = function() {
96
  var qd = getQuoteDataSafe();
97
+ var filename = (qd && qd.customer && qd.customer.orderCode ? qd.customer.orderCode : 'BG') + '.xlsx';
98
+
99
+ // Check data
100
  if (!qd || !qd.items || !qd.items.length) {
101
  if (typeof showToast === 'function') showToast('⚠️ Không có sản phẩm');
102
+ else alert('Không có sản phẩm để xuất');
103
  return;
104
  }
105
 
106
+ // Check ExcelJS
107
+ if (typeof ExcelJS === 'undefined') {
108
+ if (typeof showToast === 'function') showToast('⚠️ ExcelJS chưa tải');
109
+ else alert('Chưa tải được thư viện Excel');
 
 
 
 
 
110
  return;
111
  }
112
 
113
+ // Build xlsx rồi download
114
+ buildXLSX(qd).then(function(buf) {
115
+ if (!buf) {
116
+ if (typeof showToast === 'function') showToast('❌ Lỗi tạo file');
117
+ else alert('Lỗi tạo file Excel');
118
+ return;
119
+ }
120
+ downloadSync(buf, filename);
121
+ if (typeof showToast === 'function') showToast('✅ Đã tải ' + filename);
122
+ });
123
  };
124
 
125
+ // EXPORT DELIVERY EXCEL
126
  window.exportDeliveryExcel = function() {
127
+ var cart = window.cart || [];
128
+ var filename = 'Giao_hang.xlsx';
129
+
130
+ if (!cart.length) {
131
+ if (typeof showToast === 'function') showToast('⚠️ Giỏ hàng trống');
132
+ else alert('Giỏ hàng trống');
133
  return;
134
  }
135
 
136
+ if (typeof ExcelJS === 'undefined') {
137
+ if (typeof showToast === 'function') showToast('⚠️ ExcelJS chưa tải');
138
+ return;
 
 
 
 
 
 
 
 
 
 
 
139
  }
140
+
141
+ // Convert cart to quote format
142
+ var qd = {
143
+ items: (cart || []).map(function(c, i) {
144
+ var p = (window.D && window.D[c.idx]) ? window.D[c.idx] : {};
145
+ return {
146
+ stt: i + 1,
147
+ name: c.name || (p.name || p.n || ''),
148
+ model: c.sku || (p.sku || p.model || ''),
149
+ specs: '',
150
+ qty: c.qty || 1,
151
+ price: p.priceNum || c.priceNum || 0,
152
+ discPrice: p.priceNum || c.priceNum || 0
153
+ };
154
+ })
155
+ };
156
+
157
+ buildXLSX(qd).then(function(buf) {
158
+ if (buf) {
159
+ downloadSync(buf, filename);
160
+ if (typeof showToast === 'function') showToast('✅ Đã tải ' + filename);
161
+ }
162
+ });
163
+ };
164
 
165
+ console.log('[VAI FINAL] Excel export fixed');
166
  })();