From 15bbadf6d6baaeb6b5264e8deb76456c2fbb4265 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 19 May 2026 08:25:20 +0800 Subject: [PATCH] =?UTF-8?q?ui(dev=20SPA):=20=E6=96=87=E4=BB=B6=E7=82=B9?= =?UTF-8?q?=E5=87=BB=E5=BC=B9=E6=A1=86=E9=A2=84=E8=A7=88(image/pdf/text/md?= =?UTF-8?q?/docx/xlsx,=20=E5=85=B6=E5=AE=83=20fallback)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原行为 click → 走 downloadFile 直接落盘,不能在线看。 现 click → openFilePreview(rel) 打开 #file-preview-modal(90vw × 90vh), 按扩展名分派渲染器: - image (jpg/png/gif/webp/bmp/svg/ico) → blob URL - pdf → `; +} + +function _showText(text) { + const body = $("fp-body"); + body.className = "body"; + body.innerHTML = ""; + const pre = document.createElement("pre"); + pre.className = "preview-text"; + pre.textContent = text; + body.appendChild(pre); +} + +function _showMarkdown(text) { + const body = $("fp-body"); + body.className = "body"; + body.innerHTML = `
${renderMd(text)}
`; + highlightIn(body); +} + +async function _showDocx(blob) { + const body = $("fp-body"); + body.className = "body center"; + body.innerHTML = `
解析 docx 中…
`; + try { + await loadScript("/static/vendor/jszip.min.js"); + await loadScript("/static/vendor/docx-preview.min.js"); + } catch (e) { + _showFallback("docx 解析库加载失败:" + e.message); + return; + } + if (!window.docx || !window.docx.renderAsync) { + _showFallback("docx 解析库不可用"); + return; + } + body.className = "body"; + body.innerHTML = `
`; + try { + await window.docx.renderAsync(blob, body.querySelector(".docx-host"), null, { + inWrapper: false, + ignoreLastRenderedPageBreak: true, + }); + } catch (e) { + _showFallback("docx 渲染失败:" + e.message); + } +} + +async function _showXlsx(blob) { + const body = $("fp-body"); + body.className = "body center"; + body.innerHTML = `
解析表格中…
`; + try { + await loadScript("/static/vendor/xlsx.full.min.js"); + } catch (e) { + _showFallback("xlsx 解析库加载失败:" + e.message); + return; + } + if (!window.XLSX || !window.XLSX.read) { + _showFallback("xlsx 解析库不可用"); + return; + } + let wb; + try { + const ab = await blob.arrayBuffer(); + wb = window.XLSX.read(ab, { type: "array" }); + } catch (e) { + _showFallback("xlsx 解析失败:" + e.message); + return; + } + const names = wb.SheetNames || []; + if (!names.length) { _showFallback("xlsx 内无 sheet"); return; } + body.className = "body"; + const tabsHtml = names.map((n, i) => + `` + ).join(""); + body.innerHTML = `
${tabsHtml}
`; + const render = (i) => { + const ws = wb.Sheets[names[i]]; + $("fp-xlsx-sheet").innerHTML = window.XLSX.utils.sheet_to_html(ws); + }; + body.querySelectorAll(".xlsx-tab").forEach((btn) => { + btn.onclick = () => { + body.querySelectorAll(".xlsx-tab").forEach((b) => b.classList.remove("active")); + btn.classList.add("active"); + render(parseInt(btn.dataset.i)); + }; + }); + render(0); +} + +function _showFallback(msg) { + const body = $("fp-body"); + body.className = "body center"; + body.innerHTML = ""; + const ph = document.createElement("div"); + ph.className = "ph"; + ph.textContent = msg; + const br = document.createElement("br"); + const dl = document.createElement("button"); + dl.className = "primary"; + dl.textContent = "下载原文件"; + dl.style.marginTop = "12px"; + dl.onclick = () => { if (_fpCurrentRel) downloadFile(_fpCurrentRel); }; + ph.appendChild(document.createElement("br")); + ph.appendChild(br); + ph.appendChild(dl); + body.appendChild(ph); +} + +function closeFilePreview() { + $("file-preview-modal").classList.remove("show"); + $("fp-body").innerHTML = ""; + _flushBlobUrls(); + _fpCurrentRel = null; +} + +$("fp-close").onclick = closeFilePreview; +$("fp-download").onclick = () => { if (_fpCurrentRel) downloadFile(_fpCurrentRel); }; +$("file-preview-modal").addEventListener("click", (e) => { + if (e.target.id === "file-preview-modal") closeFilePreview(); +}); +document.addEventListener("keydown", (e) => { + if (e.key === "Escape" && $("file-preview-modal").classList.contains("show")) { + closeFilePreview(); + } +}); + async function uploadSelected() { const inp = $("upload-input"); const files = Array.from(inp.files || []);