| New file |
| | |
| | | <!DOCTYPE html> |
| | | <html lang="zh-CN"> |
| | | <head> |
| | | <meta charset="UTF-8"> |
| | | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| | | <title>Word 在线预览示例</title> |
| | | <script src="https://cdn.jsdelivr.net/npm/jszip@3.10.1/dist/jszip.min.js"></script> |
| | | <script src="https://cdn.jsdelivr.net/npm/docx-preview@0.3.6/dist/docx-preview.min.js"></script> |
| | | <style> |
| | | * { |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | body { |
| | | margin: 0; |
| | | font-family: "Microsoft YaHei", Arial, sans-serif; |
| | | color: #1f2937; |
| | | background: #f5f7fb; |
| | | } |
| | | |
| | | .page { |
| | | min-height: 100vh; |
| | | display: flex; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .toolbar { |
| | | display: grid; |
| | | grid-template-columns: 1fr auto auto auto auto; |
| | | gap: 10px; |
| | | align-items: center; |
| | | padding: 14px 18px; |
| | | background: #ffffff; |
| | | border-bottom: 1px solid #d9e0ea; |
| | | } |
| | | |
| | | .url-input { |
| | | width: 100%; |
| | | height: 38px; |
| | | padding: 0 12px; |
| | | border: 1px solid #c7d0dd; |
| | | border-radius: 6px; |
| | | color: #1f2937; |
| | | background: #ffffff; |
| | | outline: none; |
| | | } |
| | | |
| | | .api-input { |
| | | width: 260px; |
| | | } |
| | | |
| | | .url-input:focus { |
| | | border-color: #2563eb; |
| | | box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.12); |
| | | } |
| | | |
| | | .button { |
| | | height: 38px; |
| | | padding: 0 14px; |
| | | border: 1px solid #2563eb; |
| | | border-radius: 6px; |
| | | color: #ffffff; |
| | | background: #2563eb; |
| | | cursor: pointer; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | .button.secondary { |
| | | color: #1f2937; |
| | | border-color: #c7d0dd; |
| | | background: #ffffff; |
| | | } |
| | | |
| | | .tips { |
| | | padding: 10px 18px; |
| | | color: #64748b; |
| | | font-size: 13px; |
| | | line-height: 1.6; |
| | | background: #ffffff; |
| | | border-bottom: 1px solid #d9e0ea; |
| | | } |
| | | |
| | | .viewer { |
| | | flex: 1; |
| | | min-height: 0; |
| | | padding: 16px; |
| | | } |
| | | |
| | | .status { |
| | | display: none; |
| | | margin-bottom: 12px; |
| | | padding: 12px 14px; |
| | | border: 1px solid #d9e0ea; |
| | | border-radius: 6px; |
| | | color: #475569; |
| | | background: #ffffff; |
| | | } |
| | | |
| | | .status.active { |
| | | display: block; |
| | | } |
| | | |
| | | .status.error { |
| | | color: #b91c1c; |
| | | border-color: #fecaca; |
| | | background: #fff7f7; |
| | | } |
| | | |
| | | .status a { |
| | | color: #2563eb; |
| | | text-decoration: none; |
| | | } |
| | | |
| | | .docx-box, |
| | | .office-frame { |
| | | width: 100%; |
| | | min-height: calc(100vh - 140px); |
| | | border: 1px solid #d9e0ea; |
| | | border-radius: 8px; |
| | | background: #ffffff; |
| | | } |
| | | |
| | | .docx-box { |
| | | overflow: auto; |
| | | padding: 18px; |
| | | } |
| | | |
| | | .office-frame { |
| | | height: calc(100vh - 140px); |
| | | } |
| | | |
| | | .hidden { |
| | | display: none; |
| | | } |
| | | |
| | | @media (max-width: 768px) { |
| | | .toolbar { |
| | | grid-template-columns: 1fr; |
| | | padding: 12px; |
| | | } |
| | | |
| | | .api-input { |
| | | width: 100%; |
| | | } |
| | | |
| | | .button { |
| | | width: 100%; |
| | | } |
| | | |
| | | .viewer { |
| | | padding: 10px; |
| | | } |
| | | |
| | | .docx-box, |
| | | .office-frame { |
| | | min-height: 520px; |
| | | } |
| | | } |
| | | </style> |
| | | </head> |
| | | <body> |
| | | <main class="page"> |
| | | <div class="toolbar"> |
| | | <input |
| | | id="wordUrl" |
| | | class="url-input" |
| | | type="url" |
| | | value="http://xllpgzx.com/report/%E9%A1%B9%E7%9B%AE%E6%B5%8B%E8%AF%950417%E7%AC%AC%E4%B8%89%E6%96%B9%E6%A3%80%E6%9F%A5%E6%8A%A5%E5%91%8A-%E5%B2%B1%E8%A7%82.docx" |
| | | > |
| | | <input |
| | | id="apiBaseUrl" |
| | | class="url-input api-input" |
| | | type="url" |
| | | value="http://localhost:8182" |
| | | title="后端服务地址" |
| | | > |
| | | <button class="button" type="button" onclick="previewLocal()">本地预览</button> |
| | | <button class="button secondary" type="button" onclick="previewOfficeEmbed()">Office内嵌</button> |
| | | <button class="button secondary" type="button" onclick="openOriginal()">打开原文件</button> |
| | | <button class="button secondary" type="button" onclick="openOfficeViewer()">新窗口预览</button> |
| | | </div> |
| | | |
| | | <div class="tips"> |
| | | 默认通过后端代理读取 Word,再使用 docx-preview 在浏览器本地渲染。请先启动后端服务,并确认后端地址正确。 |
| | | </div> |
| | | |
| | | <section class="viewer"> |
| | | <div id="status" class="status"></div> |
| | | <div id="docxPreview" class="docx-box"></div> |
| | | <div id="officeHost" class="hidden"></div> |
| | | </section> |
| | | </main> |
| | | |
| | | <script> |
| | | var officeTimer = null; |
| | | |
| | | function getWordUrl() { |
| | | return document.getElementById("wordUrl").value.trim(); |
| | | } |
| | | |
| | | function getApiBaseUrl() { |
| | | return document.getElementById("apiBaseUrl").value.trim().replace(/\/$/, ""); |
| | | } |
| | | |
| | | function buildProxyUrl(wordUrl) { |
| | | return getApiBaseUrl() + "/admin/stat/report/wordPreviewProxy?wordUrl=" + encodeURIComponent(wordUrl); |
| | | } |
| | | |
| | | function buildOfficeEmbedUrl(wordUrl) { |
| | | return "https://view.officeapps.live.com/op/embed.aspx?src=" + encodeURIComponent(wordUrl); |
| | | } |
| | | |
| | | function buildOfficeViewUrl(wordUrl) { |
| | | return "https://view.officeapps.live.com/op/view.aspx?src=" + encodeURIComponent(wordUrl); |
| | | } |
| | | |
| | | function showStatus(message, isError) { |
| | | var status = document.getElementById("status"); |
| | | status.className = "status active" + (isError ? " error" : ""); |
| | | status.innerHTML = message; |
| | | } |
| | | |
| | | function hideStatus() { |
| | | document.getElementById("status").className = "status"; |
| | | } |
| | | |
| | | function showLocalContainer() { |
| | | document.getElementById("docxPreview").className = "docx-box"; |
| | | document.getElementById("officeHost").className = "hidden"; |
| | | } |
| | | |
| | | function showOfficeContainer() { |
| | | document.getElementById("docxPreview").className = "hidden"; |
| | | document.getElementById("officeHost").className = ""; |
| | | } |
| | | |
| | | async function previewLocal() { |
| | | var wordUrl = getWordUrl(); |
| | | if (!wordUrl) { |
| | | alert("请输入 Word 文件 URL"); |
| | | return; |
| | | } |
| | | |
| | | if (!window.docx || !window.docx.renderAsync) { |
| | | showStatus("docx-preview 加载失败,请检查 CDN 网络。", true); |
| | | return; |
| | | } |
| | | |
| | | showLocalContainer(); |
| | | showStatus("正在下载并渲染 Word 文件...", false); |
| | | |
| | | var container = document.getElementById("docxPreview"); |
| | | container.innerHTML = ""; |
| | | |
| | | try { |
| | | var response = await fetch(buildProxyUrl(wordUrl), { mode: "cors" }); |
| | | if (!response.ok) { |
| | | throw new Error("HTTP " + response.status); |
| | | } |
| | | |
| | | var blob = await response.blob(); |
| | | await window.docx.renderAsync(blob, container, null, { |
| | | className: "docx", |
| | | inWrapper: true, |
| | | ignoreWidth: false, |
| | | ignoreHeight: false, |
| | | ignoreFonts: false, |
| | | breakPages: true, |
| | | renderHeaders: true, |
| | | renderFooters: true, |
| | | renderFootnotes: true, |
| | | renderEndnotes: true, |
| | | useBase64URL: true, |
| | | experimental: true |
| | | }); |
| | | hideStatus(); |
| | | } catch (error) { |
| | | var viewerUrl = buildOfficeViewUrl(wordUrl); |
| | | showStatus( |
| | | "本地预览失败:" + error.message |
| | | + "。请确认后端服务已启动,且代理接口允许该Word地址。可使用 " |
| | | + "<a href=\"" + viewerUrl + "\" target=\"_blank\" rel=\"noopener noreferrer\">新窗口预览</a>" |
| | | + "。", |
| | | true |
| | | ); |
| | | } |
| | | } |
| | | |
| | | function previewOfficeEmbed() { |
| | | var wordUrl = getWordUrl(); |
| | | if (!wordUrl) { |
| | | alert("请输入 Word 文件 URL"); |
| | | return; |
| | | } |
| | | |
| | | var officeHost = document.getElementById("officeHost"); |
| | | var viewerUrl = buildOfficeEmbedUrl(wordUrl); |
| | | |
| | | showOfficeContainer(); |
| | | showStatus("正在加载 Office 内嵌预览...", false); |
| | | window.clearTimeout(officeTimer); |
| | | |
| | | officeHost.innerHTML = ""; |
| | | var iframe = document.createElement("iframe"); |
| | | iframe.className = "office-frame"; |
| | | iframe.title = "Office Word 在线预览"; |
| | | iframe.referrerPolicy = "no-referrer-when-downgrade"; |
| | | iframe.allowFullscreen = true; |
| | | iframe.onload = function () { |
| | | window.clearTimeout(officeTimer); |
| | | hideStatus(); |
| | | }; |
| | | iframe.src = viewerUrl; |
| | | officeHost.appendChild(iframe); |
| | | |
| | | officeTimer = window.setTimeout(function () { |
| | | showStatus( |
| | | "Office 内嵌预览仍在加载。若页面空白,请使用 " |
| | | + "<a href=\"" + buildOfficeViewUrl(wordUrl) + "\" target=\"_blank\" rel=\"noopener noreferrer\">新窗口预览</a>" |
| | | + "。", |
| | | true |
| | | ); |
| | | }, 8000); |
| | | } |
| | | |
| | | function openOriginal() { |
| | | var wordUrl = getWordUrl(); |
| | | if (wordUrl) { |
| | | window.open(wordUrl, "_blank", "noopener,noreferrer"); |
| | | } |
| | | } |
| | | |
| | | function openOfficeViewer() { |
| | | var wordUrl = getWordUrl(); |
| | | if (wordUrl) { |
| | | window.open(buildOfficeViewUrl(wordUrl), "_blank", "noopener,noreferrer"); |
| | | } |
| | | } |
| | | |
| | | previewLocal(); |
| | | </script> |
| | | </body> |
| | | </html> |