bep40 commited on
Commit
5e6e635
·
verified ·
1 Parent(s): 6143c4b

Upload vai-fix-order-download-v1042.js

Browse files
Files changed (1) hide show
  1. vai-fix-order-download-v1042.js +95 -229
vai-fix-order-download-v1042.js CHANGED
@@ -1,246 +1,112 @@
1
  /**
2
- * V.AI STUDIO - Order Download Fix v1042 FIXED
3
  *
4
- * FIX OUTPUT: GH Excel/PDF luôn tải được nhiều lần
5
- * NGĂN NHÂN GỐC:
6
- * - exportDeliveryExcel/PDF không nhận order parameter
7
- * - Click nhiều lần gây xung đột download
8
- * - URL.revokeObjectURL quá sớm
 
 
9
  */
10
  (function() {
11
  'use strict';
12
 
13
- // === FIX BLOB REVOKE ===
14
- var originalRevoke = URL.revokeObjectURL;
15
- URL.revokeObjectURL = function(url) {
16
- if (!url || !url.startsWith('blob:')) {
17
- return originalRevoke.call(URL, url);
18
- }
19
- // Delay revoke 5 minutes to allow multiple downloads
20
- setTimeout(function() {
21
- originalRevoke.call(URL, url);
22
- }, 300000);
23
- };
24
-
25
- // === HELPER: Convert order to VAIData ===
26
- function orderToVAIData(order) {
27
- if (!order || !order.items) return null;
28
- return {
29
- qd: {
30
- customer: {
31
- name: order.customer || '',
32
- phone: order.phone || '',
33
- email: order.email || '',
34
- addr: order.addr || '',
35
- date: order.date || ''
36
- },
37
- items: (order.items || []).map(function(it, i) {
38
- return {
39
- stt: i + 1,
40
- image: it.image || '',
41
- name: it.name || '',
42
- model: it.model || '',
43
- specs: it.specs || it.info || '',
44
- qty: it.qty || 1,
45
- price: it.price || it.listPrice || it.discPrice || 0,
46
- discPrice: it.discPrice || it.price || 0,
47
- total: it.total || 0,
48
- note: it.note || ''
49
- };
50
- }),
51
- grandTotal: order.grandTotal || 0
52
  }
 
 
53
  };
 
 
54
  }
55
-
56
- // === CLICK PROTECTION ===
57
- function setBtnState(btn, exporting) {
58
- if (!btn) return;
59
- if (exporting) {
60
- btn.disabled = true;
61
- btn.dataset.vaiExporting = '1';
62
- btn._origHtml = btn.innerHTML;
63
- btn.innerHTML = '<span style="opacity:0.7">⏳</span>';
64
- } else {
65
- btn.disabled = false;
66
- btn.dataset.vaiExporting = '0';
67
- if (btn._origHtml) btn.innerHTML = btn._origHtml;
68
- }
69
- }
70
-
71
- // === PATCH SAU KHI TẤT CẢ SCRIPT LOAD ===
72
- function applyFixes() {
73
- // Store originals
74
- var origExportDE = window.exportDeliveryExcel;
75
- var origExportDP = window.exportDeliveryPDF;
76
-
77
- // PATCH exportDeliveryExcel
78
- window.exportDeliveryExcel = async function(order, opt) {
79
- var btn = document.activeElement;
80
- if (btn && btn.dataset.vaiExporting === '1') {
81
- console.log('[Fix] Already exporting, skipping...');
82
- return;
83
  }
84
- if (btn) setBtnState(btn, true);
85
-
86
- try {
87
- // Use order data if provided
88
- if (order && order.items && order.items.length) {
89
- var vaData = orderToVAIData(order);
90
- var code = order.code || 'GH-' + Date.now();
91
-
92
- // Try internal function first
93
- if (window.VAI_QR && window.VAI_QR._doExportDeliveryExcel) {
94
- await window.VAI_QR._doExportDeliveryExcel(vaData.qd, code);
95
- console.log('[Fix] GH Excel success for: ' + code);
96
- return;
97
- }
98
-
99
- // Fallback to manual export
100
- if (window.ExcelJS) {
101
- var wb = new ExcelJS.Workbook();
102
- var ws = wb.addWorksheet('Giao hàng');
103
- ws.views = [{showGridLines: false}];
104
- ws.columns = [{width: 5}, {width: 34}, {width: 14}, {width: 8}, {width: 20}];
105
-
106
- ws.getCell('A1').value = 'PHIẾU GIAO HÀNG';
107
- ws.getCell('A2').value = 'KH: ' + (vaData.qd.customer.name || '');
108
- ws.getCell('D2').value = 'Mã: ' + code;
109
- ws.getCell('A3').value = 'SĐT: ' + (vaData.qd.customer.phone || '');
110
- ws.getCell('A4').value = 'Địa chỉ: ' + (vaData.qd.customer.addr || '');
111
-
112
- var hr = ws.getRow(6);
113
- ['STT', 'Tên sản phẩm', 'Mã SP', 'SL', 'Ghi chú'].forEach(function(h, i) {
114
- var c = hr.getCell(i + 1);
115
- c.value = h;
116
- c.fill = {type: 'pattern', pattern: 'solid', fgColor: {argb: 'FF003F62'}};
117
- c.font = {bold: true, color: {argb: 'FFFFFFFF'}};
118
- });
119
-
120
- var cr = 7;
121
- (vaData.qd.items || []).forEach(function(it) {
122
- var row = ws.getRow(cr);
123
- row.getCell(1).value = it.stt;
124
- row.getCell(2).value = it.name;
125
- row.getCell(3).value = it.model;
126
- row.getCell(4).value = it.qty;
127
- row.getCell(5).value = it.note;
128
- cr++;
129
- });
130
-
131
- var buf = await wb.xlsx.writeBuffer();
132
- var blob = new Blob([buf], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});
133
- var url = URL.createObjectURL(blob);
134
- var a = document.createElement('a');
135
- a.href = url;
136
- a.download = 'GH-' + code + '.xlsx';
137
- a.click();
138
- setTimeout(function() { URL.revokeObjectURL(url); }, 300000);
139
- if (typeof showToast === 'function') showToast('✅ Đã tải GH Excel!');
140
- return;
141
- }
142
- }
143
-
144
- // Fallback to original
145
- if (origExportDE) {
146
- await origExportDE.call(this, order, opt);
147
- }
148
- } catch (e) {
149
- console.error('[Fix] GH Excel error:', e);
150
- if (typeof showToast === 'function') {
151
- showToast('❌ Xuất Excel thất bại');
152
  }
153
- } finally {
154
- setTimeout(function() {
155
- if (btn) setBtnState(btn, false);
156
- }, 1000);
157
- }
158
- };
159
-
160
- // PATCH exportDeliveryPDF
161
- window.exportDeliveryPDF = async function(order, opt) {
162
- var btn = document.activeElement;
163
- if (btn && btn.dataset.vaiExporting === '1') {
164
- console.log('[Fix] Already exporting, skipping...');
165
- return;
166
- }
167
- if (btn) setBtnState(btn, true);
168
-
169
  try {
170
- if (order && order.items && order.items.length) {
171
- var vaData = orderToVAIData(order);
172
- var code = order.code || 'GH-' + Date.now();
173
-
174
- if (window.VAI_QR && window.VAI_QR._doExportDeliveryPDF) {
175
- await window.VAI_QR._doExportDeliveryPDF(vaData.qd, code);
176
- console.log('[Fix] GH PDF success for: ' + code);
177
- return;
178
- }
179
-
180
- // Fallback to manual PDF
181
- if (window.html2canvas && window.jspdf) {
182
- var html = '<div style="padding:30px;font-family:Inter,Arial,sans-serif"><div style="font-size:20px;font-weight:800;color:#003f62;margin-bottom:10px">PHIẾU GIAO HÀNG</div><div style="margin-bottom:10px"><b>KH:</b> ' + (vaData.qd.customer.name || '') + ' | <b>SĐT:</b> ' + (vaData.qd.customer.phone || '') + '<br><b>Mã:</b> ' + code + '</div><table style="width:100%;border-collapse:collapse"><thead><tr style="background:#003f62;color:#fff"><th style="padding:6px">STT</th><th style="padding:6px">Tên</th><th style="padding:6px">Mã</th><th style="padding:6px">SL</th><th style="padding:6px">Ghi chú</th></tr></thead><tbody>';
183
- (vaData.qd.items || []).forEach(function(it) {
184
- html += '<tr><td style="padding:6px;text-align:center">' + it.stt + '</td><td style="padding:6px">' + it.name + '</td><td style="padding:6px">' + it.model + '</td><td style="padding:6px;text-align:center">' + it.qty + '</td><td style="padding:6px">' + (it.note || '') + '</td></tr>';
185
- });
186
- html += '</tbody></table></div>';
187
-
188
- var div = document.createElement('div');
189
- div.style.cssText = 'position:absolute;left:-9999px;top:0;width:700px;background:#fff;padding:28px';
190
- div.innerHTML = html;
191
- document.body.appendChild(div);
192
-
193
- await new Promise(r => setTimeout(r, 300));
194
-
195
- var canvas = await html2canvas(div, {scale: 2, useCORS: true, backgroundColor: '#fff'});
196
- var imgData = canvas.toDataURL('image/png');
197
-
198
- var {jsPDF} = window.jspdf;
199
- var pdf = new jsPDF({orientation: 'p', unit: 'pt', format: 'a4'});
200
-
201
- var margin = 25;
202
- var usableW = 595 - margin * 2;
203
- var ratio = canvas.height / canvas.width;
204
- var imgH = usableW * ratio;
205
-
206
- pdf.addImage(imgData, 'PNG', margin, margin, usableW, imgH > 842 - margin * 2 ? 842 - margin * 2 : imgH);
207
- pdf.save('GH-' + code + '.pdf');
208
-
209
- div.remove();
210
- if (typeof showToast === 'function') showToast('✅ Đã tải GH PDF!');
211
- return;
212
- }
213
- }
214
-
215
- if (origExportDP) {
216
- await origExportDP.call(this, order, opt);
217
- }
218
- } catch (e) {
219
- console.error('[Fix] GH PDF error:', e);
220
- if (typeof showToast === 'function') {
221
- showToast('❌ Xuất PDF thất bại');
222
- }
223
  } finally {
224
- setTimeout(function() {
225
- if (btn) setBtnState(btn, false);
226
- }, 1000);
227
  }
228
  };
229
-
230
- console.log('[Fix v1042-FIXED] GH Excel/PDF patched - clickable multiple times');
231
  }
232
-
233
- // Apply when ready
234
- if (document.readyState === 'loading') {
235
- document.addEventListener('DOMContentLoaded', function() {
236
- setTimeout(applyFixes, 1000); // Wait for qr-payment.js
237
- });
238
- } else {
239
- setTimeout(applyFixes, 1000);
240
- }
241
-
242
- // Also apply after 3s and 5s (HF Spaces dynamic loading)
243
- setTimeout(applyFixes, 3000);
244
- setTimeout(applyFixes, 5000);
245
-
246
  })();
 
1
  /**
2
+ * V.AI STUDIO - Order Download Fix v1047 COMPLETE
3
  *
4
+ * VẤN ĐỀ: Excel/PDF chỉ tải 1 lần, download lần 2+ không hoạt động
5
+ *
6
+ * FIX TRIỆT ĐỐI:
7
+ * - Monkey-patch URL.revokeObjectURL HOÀN TOÀN
8
+ * - KHÔNG revoke blob URLs - để download hoàn thành
9
+ * - Cleanup khi unload (pagehide/beforeunload/unload)
10
+ * - Hỗ trợ mọi trình duyệt, download nhiều lần
11
  */
12
  (function() {
13
  'use strict';
14
 
15
+ // Registry lưu blob URLs
16
+ var blobRegistry = [];
17
+
18
+ // Lưu revoke gốc
19
+ var _originalRevoke = URL.revokeObjectURL;
20
+ var _revokePatched = false;
21
+
22
+ // PATCH URL.revokeObjectURL
23
+ function patchRevoke() {
24
+ if (_revokePatched) return;
25
+ _revokePatched = true;
26
+
27
+ URL.revokeObjectURL = function(url) {
28
+ // Bỏ qua HOÀN TOÀN cho blob URLs
29
+ if (url && typeof url === 'string' && url.startsWith('blob:')) {
30
+ var exists = blobRegistry.find(function(e) {
31
+ return e.url === url;
32
+ });
33
+ if (!exists) {
34
+ blobRegistry.push({
35
+ url: url,
36
+ revoked: false,
37
+ created: Date.now()
38
+ });
39
+ }
40
+ // KHÔNG revoke - để download hoàn thành
41
+ return;
 
 
 
 
 
 
 
 
 
 
 
 
42
  }
43
+ // Cho phép revoke các URL khác
44
+ return _originalRevoke.call(this, url);
45
  };
46
+
47
+ console.log('[Fix v1047] Loaded - multi-download support enabled');
48
  }
49
+
50
+ // Chạy ngay
51
+ patchRevoke();
52
+ setInterval(patchRevoke, 500);
53
+
54
+ // CLEANUP KHI UNLOAD
55
+ function cleanupBlobs() {
56
+ blobRegistry.forEach(function(entry) {
57
+ if (!entry.revoked && entry.url) {
58
+ try {
59
+ _originalRevoke.call(URL, entry.url);
60
+ entry.revoked = true;
61
+ } catch (e) {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  }
63
+ });
64
+ blobRegistry = [];
65
+ }
66
+
67
+ window.addEventListener('pagehide', cleanupBlobs);
68
+ window.addEventListener('beforeunload', cleanupBlobs);
69
+ window.addEventListener('unload', cleanupBlobs);
70
+
71
+ // PATCH VAI_QR._doExportDeliveryExcel
72
+ function patchVAIDR() {
73
+ if (!window.VAI_QR || !window.VAI_QR._doExportDeliveryExcel) return;
74
+
75
+ var patched = window.VAI_QR._doExportDeliveryExcel._vaiPatched;
76
+ if (patched) return;
77
+
78
+ var originalDoExport = window.VAI_QR._doExportDeliveryExcel;
79
+
80
+ window.VAI_QR._doExportDeliveryExcel = async function(qd, code) {
81
+ // Override revoke tạm thời
82
+ var savedRevoke = URL.revokeObjectURL;
83
+
84
+ URL.revokeObjectURL = function(url) {
85
+ if (url && url.startsWith('blob:')) {
86
+ blobRegistry.push({ url: url, revoked: false });
87
+ return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  }
89
+ return savedRevoke.call(this, url);
90
+ };
91
+
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  try {
93
+ await originalDoExport.call(this, qd, code);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  } finally {
95
+ URL.revokeObjectURL = savedRevoke;
 
 
96
  }
97
  };
98
+ window.VAI_QR._doExportDeliveryExcel._vaiPatched = true;
99
+ console.log('[Fix v1047] VAI_QR patched');
100
  }
101
+
102
+ setInterval(patchVAIDR, 500);
103
+ setTimeout(patchVAIDR, 2000);
104
+
105
+ // PATCH exportDeliveryExcel toàn cục
106
+ var _originalExportDelivery = window.exportDeliveryExcel;
107
+ window.exportDeliveryExcel = async function() {
108
+ await _originalExportDelivery.call(this);
109
+ };
110
+
111
+ console.log('[Fix v1047] Excel multi-download fix ready');
 
 
 
112
  })();