liujie
2026-07-22 8b3f90f178526781188ff37ae460e93eccaa12cd
update
1个文件已添加
2个文件已修改
343 ■■■■■ 已修改文件
Word在线预览示例.html 343 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin-server/src/main/resources/templates/第三方评估报告new.docx 补丁 | 查看 | 原始文档 | blame | 历史
admin-server/src/main/resources/templates/设备风险评估报告new.docx 补丁 | 查看 | 原始文档 | blame | 历史
Word在线预览示例.html
New file
@@ -0,0 +1,343 @@
<!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>
admin-server/src/main/resources/templates/第三方评估报告new.docx
Binary files differ
admin-server/src/main/resources/templates/设备风险评估报告new.docx
Binary files differ