Download excel-multi-fix-complete.js from bep40/V.AISTUDIO: direct link, hf CLI and curl.
- Browser
- Download file 10.4 kB
-
https://huggingface.co/bep40/V.AISTUDIO/resolve/refs%2Fpr%2F21/excel-multi-fix-complete.js
- Command line
-
hf download hf://bep40/V.AISTUDIO@refs/pr/21/excel-multi-fix-complete.js
-
curl -L -o excel-multi-fix-complete.js https://huggingface.co/bep40/V.AISTUDIO/resolve/refs%2Fpr%2F21/excel-multi-fix-complete.js
10.4 kB
| /** | |
| * V.AI STUDIO — Excel Multi-Download Complete Fix | |
| * ============================================ | |
| * FIX TRIỆT ĐỐI cho vấn đề: File Excel chỉ tải được 1 lần | |
| * | |
| * Áp dụng cho: | |
| * - Modal "Lập báo giá" -> nút 💾 Excel | |
| * - Modal "Đơn hàng" -> nút 💾 Lưu, GH Excel | |
| */ | |
| (function() { | |
| 'use strict'; | |
| // Registry quản lý blob URLs | |
| var blobRegistry = []; | |
| var isUnloading = false; | |
| // Cleanup khi tab đóng | |
| function cleanupAllBlobs() { | |
| isUnloading = true; | |
| blobRegistry.forEach(function(entry) { | |
| if (!entry.revoked && entry.url) { | |
| try { URL.revokeObjectURL(entry.url); entry.revoked = true; } catch (e) {} | |
| } | |
| }); | |
| blobRegistry = []; | |
| } | |
| window.addEventListener('pagehide', cleanupAllBlobs); | |
| window.addEventListener('beforeunload', cleanupAllBlobs); | |
| window.addEventListener('unload', cleanupAllBlobs); | |
| // PATCH URL.revokeObjectURL - không revoke blob URLs khi chưa unload | |
| (function() { | |
| var origRevoke = URL.revokeObjectURL; | |
| URL.revokeObjectURL = function(url) { | |
| if (isUnloading || !url || !url.startsWith('blob:')) { | |
| return origRevoke.apply(this, arguments); | |
| } | |
| // Chỉ đăng ký, không revoke | |
| var found = blobRegistry.find(function(e) { return e.url === url; }); | |
| if (!found) { | |
| blobRegistry.push({ url: url, revoked: false, created: Date.now() }); | |
| } | |
| }; | |
| })(); | |
| // PATCH exportExcel - Báo giá | |
| var origExportExcel = window.exportExcel; | |
| window.exportExcel = async function() { | |
| if (typeof ExcelJS === 'undefined') { | |
| if (typeof showToast === 'function') showToast('⚠️ Chưa có ExcelJS'); | |
| return; | |
| } | |
| // Lấy dữ liệu | |
| var d, qd, code, qrUrl; | |
| if (window.VAI_QR && typeof window.VAI_QR.getData === 'function') { | |
| d = window.VAI_QR.getData(); | |
| qd = d.qd; | |
| code = window.VAI_QR.getEffectiveOrderCode(); | |
| qrUrl = window.VAI_QR.getQRUrl(d.remaining || d.grandTotal, code); | |
| } else if (window._origGetQuoteData) { | |
| qd = window._origGetQuoteData(); | |
| code = 'VAS-' + Date.now(); | |
| d = { grandTotal: qd.grandTotal || 0, remaining: qd.grandTotal || 0 }; | |
| } else { | |
| if (typeof showToast === 'function') showToast('⚠️ Lỗi dữ liệu'); | |
| return; | |
| } | |
| // TẠO MỚI hoàn toàn | |
| var wb = new ExcelJS.Workbook(); | |
| var ws = wb.addWorksheet('Báo giá'); | |
| ws.views = [{ showGridLines: false }]; | |
| ws.columns = [{ width: 5 }, { width: 34 }, { width: 18 }, { width: 10 }, { width: 15 }]; | |
| // Header | |
| ws.mergeCells('A1:E1'); | |
| ws.getCell('A1').value = 'BẢNG BÁO GIÁ V.AI STUDIO'; | |
| ws.getCell('A1').font = { bold: true, size: 16, color: { argb: 'FF003F62' } }; | |
| ws.getCell('A1').alignment = { horizontal: 'center', vertical: 'middle' }; | |
| ws.getCell('A1').fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFF0F7FF' } }; | |
| // Customer info | |
| ws.getCell('A2').value = 'Khách hàng:'; | |
| ws.mergeCells('B2:D2'); | |
| ws.getCell('B2').value = (qd.customer ? qd.customer.name : '') || ''; | |
| ws.getCell('A3').value = 'SĐT:'; | |
| ws.getCell('B3').value = (qd.customer ? qd.customer.phone : '') || ''; | |
| ws.getCell('D3').value = 'Mã đơn: ' + code; | |
| ws.getCell('A4').value = 'Địa chỉ:'; | |
| ws.mergeCells('B4:E4'); | |
| ws.getCell('B4').value = (qd.customer ? qd.customer.addr : '') || ''; | |
| // Header row | |
| var hr = ws.getRow(6); | |
| ['STT', 'Tên sản phẩm', 'Mã SP', 'SL', 'Giá'].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' } }; | |
| }); | |
| // Items | |
| var cr = 7, total = 0; | |
| (qd.items || []).forEach(function(it, i) { | |
| var row = ws.getRow(cr); | |
| row.values = [i + 1, it.name || '', it.model || '', it.qty || 1, (it.discPrice || it.price || 0) * (it.qty || 1)]; | |
| if ((it.discPrice || it.price) > 0) { | |
| row.getCell(5).numFmt = '#,##0"đ"'; | |
| } | |
| row.getCell(5).font = { bold: true }; | |
| total += (it.discPrice || it.price || 0) * (it.qty || 1); | |
| cr++; | |
| }); | |
| // Total | |
| ws.mergeCells(cr, 1, cr, 4); | |
| ws.getCell('A' + cr).value = 'TỔNG CỘNG'; | |
| ws.getCell('A' + cr).font = { bold: true, size: 12 }; | |
| ws.getCell('E' + cr).value = total; | |
| ws.getCell('E' + cr).font = { bold: true, size: 14, color: { argb: 'FF003F62' } }; | |
| ws.getCell('E' + cr).numFmt = '#,##0"đ"'; | |
| // Download - KHÔNG revoke | |
| var buf = await wb.xlsx.writeBuffer(); | |
| var blob = new Blob([buf], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }); | |
| var url = URL.createObjectURL(blob); | |
| // Đăng ký để cleanup khi unload | |
| blobRegistry.push({ url: url, revoked: false, created: Date.now() }); | |
| var a = document.createElement('a'); | |
| a.href = url; | |
| a.download = code + '.xlsx'; | |
| document.body.appendChild(a); | |
| a.click(); | |
| setTimeout(function() { document.body.removeChild(a); }, 100); | |
| if (typeof showToast === 'function') { | |
| showToast('✅ Đã tải ' + code + '.xlsx'); | |
| } | |
| }; | |
| // PATCH exportDeliveryExcel - Giao hàng | |
| var origExportDeliveryExcel = window.exportDeliveryExcel; | |
| window.exportDeliveryExcel = async function(order, opt) { | |
| if (typeof ExcelJS === 'undefined') { | |
| if (typeof showToast === 'function') showToast('⚠️ Chưa có ExcelJS'); | |
| return; | |
| } | |
| // Lấy dữ liệu qd | |
| var qd; | |
| if (order && order.items) { | |
| qd = { | |
| customer: { | |
| name: order.customer || '', | |
| phone: order.phone || '', | |
| addr: order.addr || '', | |
| date: order.date || '' | |
| }, | |
| items: (order.items || []).map(function(it, i) { | |
| return { | |
| stt: i + 1, | |
| name: it.name || '', | |
| model: it.model || '', | |
| qty: it.qty || 1, | |
| note: it.note || '' | |
| }; | |
| }) | |
| }; | |
| } else if (window._origGetQuoteData) { | |
| qd = window._origGetQuoteData(); | |
| } else if (window.VAI_QR && window.VAI_QR.getData) { | |
| qd = window.VAI_QR.getData().qd; | |
| } else { | |
| if (typeof showToast === 'function') showToast('⚠️ Lỗi dữ liệu'); | |
| return; | |
| } | |
| var code = (order && order.code) ? order.code : | |
| (window.VAI_QR ? window.VAI_QR.getEffectiveOrderCode() : 'VAS-GH'); | |
| // TẠO MỚI hoàn toàn | |
| var wb = new ExcelJS.Workbook(); | |
| var ws = wb.addWorksheet('Giao hàng'); | |
| ws.views = [{ showGridLines: false }]; | |
| ws.columns = [{ width: 5 }, { width: 34 }, { width: 14 }, { width: 8 }, { width: 20 }]; | |
| // Header | |
| ws.mergeCells('A1:E1'); | |
| ws.getCell('A1').value = 'PHIẾU GIAO HÀNG'; | |
| ws.getCell('A1').font = { bold: true, size: 16, color: { argb: 'FF003F62' } }; | |
| ws.getCell('A1').alignment = { horizontal: 'center' }; | |
| ws.getCell('A1').fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFF0F7FF' } }; | |
| // Customer info | |
| ws.getCell('A2').value = 'KH: ' + (qd.customer ? qd.customer.name : ''); | |
| ws.getCell('D2').value = 'Mã: ' + code; | |
| ws.getCell('A3').value = 'SĐT: ' + (qd.customer ? qd.customer.phone : ''); | |
| ws.getCell('D3').value = 'Ngày: ' + (qd.customer ? (qd.customer.date || '') : ''); | |
| ws.getCell('A4').value = 'Địa chỉ: ' + (qd.customer ? (qd.customer.addr || '') : ''); | |
| // Header row | |
| 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' } }; | |
| }); | |
| // Items | |
| var cr = 7, totalQty = 0; | |
| (qd.items || []).forEach(function(it, i) { | |
| var row = ws.getRow(cr); | |
| row.values = [i + 1, it.name || '', it.model || '', it.qty || 1, it.note || '']; | |
| totalQty += (it.qty || 1); | |
| cr++; | |
| }); | |
| // Total | |
| var tr = ws.getRow(cr); | |
| ws.mergeCells(cr, 1, cr, 3); | |
| ws.getCell('A' + cr).value = 'TỔNG CỘNG SỐ LƯỢNG'; | |
| ws.getCell('D' + cr).value = totalQty; | |
| // Signature | |
| cr += 2; | |
| ws.getCell('A' + cr).value = 'Bên giao: ________________'; | |
| ws.getCell('D' + cr).value = 'Bên nhận: ________________'; | |
| // Download - KHÔNG revoke | |
| var buf = await wb.xlsx.writeBuffer(); | |
| var blob = new Blob([buf], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }); | |
| var url = URL.createObjectURL(blob); | |
| // Đăng ký để cleanup khi unload | |
| blobRegistry.push({ url: url, revoked: false, created: Date.now() }); | |
| var a = document.createElement('a'); | |
| a.href = url; | |
| a.download = 'GH-' + code + '.xlsx'; | |
| document.body.appendChild(a); | |
| a.click(); | |
| setTimeout(function() { document.body.removeChild(a); }, 100); | |
| if (typeof showToast === 'function') { | |
| showToast('✅ Đã tải GH-' + code + '.xlsx'); | |
| } | |
| }; | |
| console.log('[Excel Multi-Fix Complete] Loaded - Supports unlimited downloads!'); | |
| })(); |