文件预览页面demo完成,访问路径为:/rules/new1

This commit is contained in:
2025-04-17 17:45:55 +08:00
parent 5e143d9755
commit 119f9197b2
3 changed files with 141 additions and 469 deletions
+141 -191
View File
@@ -1,40 +1,83 @@
/**
* 文档预览与内容抽取模块
*
* 依赖包说明:
* 1. react-pdf - PDF文档预览
* 安装命令: npm install react-pdf
* 或: yarn add react-pdf
*
* 2. mammoth - Word文档转HTML预览
* 安装命令: npm install mammoth
* 或: yarn add mammoth
*
* 3. @remix-run/react, @remix-run/node - Remix框架组件
* 安装命令: npm install @remix-run/react @remix-run/node
* 或: yarn add @remix-run/react @remix-run/node
*
* 注意事项:
* - react-pdf需要pdfjs-dist作为依赖,安装react-pdf时会自动安装
* - 需要引入PDF.js worker文件,本代码通过CDN方式引入
* - 如需本地加载PDF.js worker文件,请安装pdfjs-dist并修改worker配置
*/
import { useState, useEffect, useRef } from "react";
import { useLoaderData } from "@remix-run/react";
import { Document, Page, pdfjs } from "react-pdf";
import type { LoaderFunctionArgs } from "@remix-run/node";
import mammoth from "mammoth";
// 设置 pdfjs 工作线程
/**
* 设置 pdfjs 工作线程
* 使用 CDN 上的 worker.js 文件处理 PDF 解析
*/
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;
// 模拟后端返回的抽取内容数据
/**
* 模拟后端返回的文档抽取内容数据
* 实际应用中应从API获取
*/
const mockExtractedContent = [
{ id: 1, text: "合同条款", page: 2, position: { start: 50, end: 60 } },
{ id: 2, text: "签署日期", page: 5, position: { start: 120, end: 130 } },
{ id: 3, text: "责任划分", page: 3, position: { start: 80, end: 90 } },
];
/**
* 文档抽取内容接口定义
*/
interface ExtractedContent {
id: number;
text: string;
page: number;
position: { start: number; end: number };
id: number; // 内容唯一标识
text: string; // 抽取的文本内容
page: number; // 所在页码
position: { // 在页面中的位置信息
start: number;
end: number;
};
}
/**
* Loader 函数返回数据接口定义
*/
interface LoaderData {
fileUrl: string;
initialPage: number;
extractedContent: ExtractedContent[];
fileType: "pdf" | "docx";
urls: Record<string, string>;
fileUrl: string; // 当前文档URL
initialPage: number; // 初始页码
extractedContent: ExtractedContent[]; // 抽取内容数组
fileType: "pdf" | "docx"; // 文档类型
urls: Record<string, string>; // 可用文档URL列表
}
// 定义文档加载成功回调类型
/**
* PDF文档加载成功回调接口
*/
interface DocumentLoadSuccess {
numPages: number;
numPages: number; // 文档总页数
}
// 根据URL判断文件类型
/**
* 根据URL判断文件类型
* @param url 文档URL
* @returns 文档类型:"pdf" 或 "docx"
*/
function getFileTypeFromUrl(url: string): "pdf" | "docx" {
const lowerCaseUrl = url.toLowerCase();
if (lowerCaseUrl.endsWith(".pdf")) {
@@ -46,15 +89,15 @@ function getFileTypeFromUrl(url: string): "pdf" | "docx" {
return "pdf";
}
// Remix Loader 函数
/**
* Remix Loader 函数 - 请求处理和数据加载
*/
export const loader = async ({ request }: LoaderFunctionArgs) => {
// 从URL获取查询参数
const url = new URL(request.url);
const page = url.searchParams.get("page") || 1;
// 实际文档 URL (PDF示例)
// const fileUrl = "http://172.18.0.100:9000/docauditai/documents/%E5%90%88%E5%90%8C%E6%96%87%E6%A1%A3/2025/04%E6%9C%8816%E6%97%A5/%E7%AC%AC16%E5%8F%B7--%E9%94%80%E5%94%AE%E6%97%A0%E6%A0%87%E5%BF%97%E5%A4%96%E5%9B%BD%E5%8D%B7%E7%83%9F_10%E6%97%B626%E5%88%8632%E7%A7%92/%E7%AC%AC16%E5%8F%B7--%E9%94%80%E5%94%AE%E6%97%A0%E6%A0%87%E5%BF%97%E5%A4%96%E5%9B%BD%E5%8D%B7%E7%83%9F.pdf";
// 示例文档URLs
// 示例文档URLs集合
const urls = {
// 1. 原始文档URL - 可能有CORS限制
original: "https://dev-xc-enroll.oss-cn-guangzhou.aliyuncs.com/uploads/7840-230620112939.docx",
@@ -64,127 +107,108 @@ export const loader = async ({ request }: LoaderFunctionArgs) => {
proxy: "https://dev-xc-enroll.oss-cn-guangzhou.aliyuncs.com/uploads/7840-230620112939.docx",
// 4. 本地服务器上的文档 (假设已经部署)
local: "/uploads/sample.docx",
// 5. PDF示例 (如果Word文档问题无法解决)
// 5. PDF示例
pdf: "http://172.18.0.100:9000/docauditai/documents/%E5%90%88%E5%90%8C%E6%96%87%E6%A1%A3/2025/04%E6%9C%8816%E6%97%A5/%E7%AC%AC16%E5%8F%B7--%E9%94%80%E5%94%AE%E6%97%A0%E6%A0%87%E5%BF%97%E5%A4%96%E5%9B%BD%E5%8D%B7%E7%83%9F_10%E6%97%B626%E5%88%8632%E7%A7%92/%E7%AC%AC16%E5%8F%B7--%E9%94%80%E5%94%AE%E6%97%A0%E6%A0%87%E5%BF%97%E5%A4%96%E5%9B%BD%E5%8D%B7%E7%83%9F.pdf"
};
// 使用本地文档或通过CORS代理的URL
const fileUrl = urls.public; // 可以切换到其他URL进行测试
// 使用默认文档URL
const fileUrl = urls.pdf;
// 判断文件类型
const fileType = getFileTypeFromUrl(fileUrl);
// 返回加载的数据
return {
fileUrl,
initialPage: Number(page),
extractedContent: mockExtractedContent,
fileType,
urls // 传递所有URL供前端选择
urls
};
};
/**
* 文档预览组件
*/
export default function Documents() {
// 从loader获取数据
const { fileUrl, extractedContent, fileType, urls } = useLoaderData<LoaderData>();
const [numPages, setNumPages] = useState<number | null>(null);
const [scrollToPage, setScrollToPage] = useState<number | null>(null);
const [docxLoading, setDocxLoading] = useState(false); // 设置为false以避免加载指示器
const [loadError, setLoadError] = useState<string | null>(null);
const [debugInfo, setDebugInfo] = useState<string[]>([]);
const docxContainerRef = useRef<HTMLDivElement>(null);
const [docxContentPositions, setDocxContentPositions] = useState<{[id: number]: number}>({});
const [currentUrl, setCurrentUrl] = useState<string>(fileUrl);
// 默认使用iframe模式
const [showIframe, setShowIframe] = useState<boolean>(true);
const [docxHtml, setDocxHtml] = useState<string>("");
// 状态管理
const [numPages, setNumPages] = useState<number | null>(null); // PDF总页数
const [scrollToPage, setScrollToPage] = useState<number | null>(null); // 滚动目标页码
const [docxLoading, setDocxLoading] = useState(false); // Word文档加载状态
const [loadError, setLoadError] = useState<string | null>(null); // 加载错误信息
const [debugInfo, setDebugInfo] = useState<string[]>([]); // 调试信息
const [docxHtml, setDocxHtml] = useState<string>(""); // 转换后的HTML内容
const [currentUrl, setCurrentUrl] = useState<string>(fileUrl); // 当前文档URL
// 引用
const docxContainerRef = useRef<HTMLDivElement>(null); // Word文档容器引用
// 处理抽取内容点击
/**
* 处理抽取内容点击事件 - 仅对PDF文档生效
* @param item 被点击的抽取内容项
*/
const handleContentClick = (item: ExtractedContent) => {
setScrollToPage(item.page);
// 仅对PDF文档执行交互操作
if (fileType === "pdf") {
// 使用ID滚动到指定页面
setScrollToPage(item.page);
// 对于PDF,滚动到指定页面
const pageElement = document.getElementById(`page-${item.page}`);
if (pageElement) {
pageElement.scrollIntoView({ behavior: 'smooth' });
}
} else if (fileType === "docx" && !showIframe) {
// 对于Word文档,滚动到提取内容位置 (仅本地渲染模式)
const position = docxContentPositions[item.id];
if (position !== undefined && docxContainerRef.current) {
// 找到Word内容容器内的位置并滚动
docxContainerRef.current.scrollTop = position;
// 高亮显示这个区域(模拟)
highlightDocxContent(item);
}
} else if (fileType === "docx" && showIframe) {
// 对于iframe中的Word文档,我们只能切换到特定iframe页面
// 这里我们无法控制iframe内部的滚动,只能提示用户
addDebugInfo(`在iframe中无法直接定位到"${item.text}",请在文档中手动查找`);
}
// DOCX文档不执行任何交互操作
};
// 模拟在Word文档中高亮内容
const highlightDocxContent = (item: ExtractedContent) => {
// 移除之前的高亮
const previousHighlights = document.querySelectorAll('.docx-highlight');
previousHighlights.forEach(el => el.classList.remove('docx-highlight'));
// 由于我们没有确切的位置信息,这里使用一个模拟的方法
// 实际项目中,您需要一个更精确的方法来找到文本位置
if (docxContainerRef.current) {
const textNodes = Array.from(docxContainerRef.current.querySelectorAll('p, span, div'))
.filter(node => node.textContent?.includes(item.text));
textNodes.forEach(node => {
node.classList.add('docx-highlight');
});
}
};
// PDF文档加载成功回调
/**
* PDF文档加载成功回调函数
* @param param0 包含numPages的对象
*/
function onDocumentLoadSuccess({ numPages }: DocumentLoadSuccess) {
setNumPages(numPages);
console.log("PDF加载成功,页数:", numPages);
}
// 简化的调试日志
/**
* 添加调试信息
* @param info 调试信息文本
*/
const addDebugInfo = (info: string) => {
console.log(info);
setDebugInfo(prev => [...prev, `${new Date().toISOString().split('T')[1].split('.')[0]}: ${info}`]);
};
// 切换到不同的文档URL
/**
* 切换文档URL
* @param urlKey URL键名
*/
const switchDocumentUrl = (urlKey: keyof typeof urls) => {
setCurrentUrl(urls[urlKey]);
setDebugInfo([]);
setLoadError(null);
setDocxLoading(false);
setShowIframe(true);
addDebugInfo(`切换到新的文档URL: ${urls[urlKey]}`);
};
// 切换到iframe模式 (当直接加载文档有CORS问题时)
const switchToIframeMode = () => {
setShowIframe(true);
setDocxLoading(false);
addDebugInfo("切换到iframe嵌入模式");
};
// 使用mammoth处理Word文档
/**
* Word文档处理逻辑
*/
useEffect(() => {
if (fileType === "docx" && docxContainerRef.current && !showIframe) {
if (fileType === "docx" && docxContainerRef.current) {
setDocxLoading(true);
setDebugInfo([]); // 清空之前的调试信息
setDebugInfo([]); // 清空调试信息
addDebugInfo(`准备加载Word文档: ${currentUrl}`);
const loadDocx = async () => {
try {
// 获取文件
// 1. 获取文档文件
addDebugInfo(`开始获取文件...`);
let response;
try {
response = await fetch(currentUrl, {
// 添加CORS相关选项
mode: 'cors',
credentials: 'omit',
headers: {
@@ -197,12 +221,13 @@ export default function Documents() {
throw new Error(`网络请求失败: ${fetchError instanceof Error ? fetchError.message : String(fetchError)}`);
}
// 检查响应状态
if (!response.ok) {
throw new Error(`文档无法访问,状态码: ${response.status}`);
}
addDebugInfo(`文档下载成功,状态码: ${response.status}`);
// 转换为ArrayBuffer
// 2. 将响应转换为ArrayBuffer
addDebugInfo(`开始读取响应内容为ArrayBuffer...`);
let buffer;
try {
@@ -213,10 +238,10 @@ export default function Documents() {
throw new Error(`转换文档内容失败: ${bufferError instanceof Error ? bufferError.message : String(bufferError)}`);
}
// 使用mammoth.js将Word转换为HTML,添加自定义选项
// 3. 使用mammoth.js将Word转换为HTML
addDebugInfo("使用mammoth开始转换文档为HTML...");
try {
// 添加自定义样式映射
// 自定义样式映射
const styleMap = `
p[style-name='Heading 1'] => h1:fresh
p[style-name='Heading 2'] => h2:fresh
@@ -225,13 +250,14 @@ export default function Documents() {
table => table.docx-table
`;
// 创建简化版的转换选项
// 转换选项
const options = {
arrayBuffer: buffer,
styleMap: styleMap,
includeDefaultStyleMap: true
};
// 执行转换
const result = await mammoth.convertToHtml(options);
// 检查转换警告
@@ -243,60 +269,18 @@ export default function Documents() {
addDebugInfo("文档转换成功,获取到HTML内容");
// 为生成的HTML文档添加包装容器和样式
// 4. 为生成的HTML添加包装容器和样式
const enhancedHtml = `
<div class="document-container">
${result.value}
<div class="format-note">
<p>注意:本地转换使用了简化版格式,一些高级格式(如页眉页脚、复杂表格式)可能无法完全显示。</p>
<p>如需查看完整格式,请使用"嵌入模式"或下载文档。</p>
<p>注意:部分复杂格式(如页眉页脚、复杂表格式)可能无法完全显示。</p>
</div>
</div>
`;
// 存储HTML内容
// 更新状态
setDocxHtml(enhancedHtml);
// 查找匹配的内容并创建位置映射
setTimeout(() => {
try {
if (docxContainerRef.current) {
const positionsMap: {[id: number]: number} = {};
extractedContent.forEach((item) => {
// 在HTML内容中查找文本
// 使用更安全的查询方式
if (docxContainerRef.current) {
// 获取所有可能包含文本的元素
const elements = docxContainerRef.current.querySelectorAll('p, h1, h2, h3, h4, h5, h6, li, td, th, span');
// 转为数组并过滤包含目标文本的元素
const textElements = Array.from(elements).filter(element =>
element.textContent?.includes(item.text)
);
if (textElements.length > 0) {
// 使用找到的第一个元素的位置
const element = textElements[0];
const rect = element.getBoundingClientRect();
const containerRect = docxContainerRef.current.getBoundingClientRect();
// 计算相对于容器的位置
positionsMap[item.id] = rect.top - containerRect.top + docxContainerRef.current.scrollTop;
// 标记找到的元素
element.classList.add('docx-content-found');
}
}
});
setDocxContentPositions(positionsMap);
addDebugInfo(`已创建 ${Object.keys(positionsMap).length} 个内容位置映射`);
}
} catch (positionError) {
addDebugInfo(`创建位置映射时出错: ${positionError instanceof Error ? positionError.message : String(positionError)}`);
}
}, 500);
setDocxLoading(false);
} catch (mammothError) {
addDebugInfo(`Mammoth转换失败: ${mammothError instanceof Error ? mammothError.message : String(mammothError)}`);
@@ -312,9 +296,11 @@ export default function Documents() {
loadDocx();
}
}, [currentUrl, fileType, extractedContent, showIframe]);
}, [currentUrl, fileType]);
// 页面渲染完成后检查是否需要滚动
/**
* 页面滚动逻辑
*/
useEffect(() => {
if (scrollToPage && fileType === "pdf") {
const pageElement = document.getElementById(`page-${scrollToPage}`);
@@ -325,7 +311,10 @@ export default function Documents() {
}
}, [scrollToPage, fileType]);
// 生成所有PDF页面的数组
/**
* 生成所有PDF页面的渲染数组
* @returns 页面组件数组
*/
const renderAllPages = () => {
if (!numPages) return null;
@@ -347,45 +336,14 @@ export default function Documents() {
};
return (
<div className="flex min-h-screen bg-gray-50 p-6">
<div className="flex h-screen bg-gray-50">
{/* 文档展示区域 */}
<div className="flex-1 mr-6">
<div className="bg-white p-4 rounded-lg shadow-md">
<div className="flex-1 mr-6 p-4">
<div className="bg-white p-4 rounded-lg shadow-md h-full flex flex-col">
<h1 className="text-2xl font-bold mb-4"> ({fileType.toUpperCase()})</h1>
{fileType === "docx" && (
<div className="bg-gray-100 p-3 mb-4 rounded flex flex-col">
<div className="flex justify-between items-center mb-2">
<p className="text-sm text-gray-600">Word文档预览模式</p>
<div className="flex gap-2">
<button
onClick={() => setShowIframe(!showIframe)}
className={`px-3 py-1 text-sm rounded ${showIframe ? 'bg-gray-200' : 'bg-blue-500 text-white'}`}
>
{showIframe ? "尝试本地渲染" : "使用嵌入模式"}
</button>
<button
onClick={() => window.open(currentUrl, '_blank')}
className="px-3 py-1 bg-gray-500 text-white text-sm rounded"
>
</button>
</div>
</div>
{!showIframe && (
<div className="text-xs text-gray-500 bg-yellow-50 p-2 rounded">
<p></p>
<ul className="list-disc pl-5 mt-1">
<li>使mammoth.js库将Word文档转换为HTML</li>
<li></li>
<li>使Google Docs提供原生渲染</li>
</ul>
</div>
)}
</div>
)}
<div className="w-full h-[80vh] overflow-auto bg-gray-100 rounded-lg p-4">
{/* 文档内容显示区域 */}
<div className="w-full flex-1 overflow-auto bg-gray-100 rounded-lg p-4">
{loadError ? (
<div className="text-red-500 flex flex-col items-center justify-center h-full">
<p className="mb-4">:</p>
@@ -405,9 +363,6 @@ export default function Documents() {
<button onClick={() => switchDocumentUrl('proxy')} className="px-3 py-1 bg-blue-500 text-white rounded">
使CORS代理
</button>
<button onClick={() => switchToIframeMode()} className="px-3 py-1 bg-purple-500 text-white rounded">
使iframe嵌入
</button>
<button onClick={() => switchDocumentUrl('pdf')} className="px-3 py-1 bg-yellow-500 text-white rounded">
PDF
</button>
@@ -418,6 +373,7 @@ export default function Documents() {
</div>
</div>
) : fileType === "pdf" ? (
/* PDF 文档渲染 */
<Document
file={currentUrl}
onLoadSuccess={onDocumentLoadSuccess}
@@ -433,8 +389,10 @@ export default function Documents() {
{renderAllPages()}
</Document>
) : (
/* Word 文档渲染 */
<>
{docxLoading ? (
/* 加载状态显示 */
<div className="flex flex-col items-center justify-center h-full">
<div className="mb-6">
<div className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-blue-500"></div>
@@ -449,18 +407,8 @@ export default function Documents() {
</div>
)}
</div>
) : showIframe ? (
// 嵌入模式显示Word文档
<div className="w-full h-full">
<iframe
src={`https://docs.google.com/viewer?url=${encodeURIComponent(currentUrl)}&embedded=true`}
className="w-full h-full"
frameBorder="0"
title="谷歌文档查看器"
/>
</div>
) : (
// 本地渲染模式 (只有用户特别点击按钮才显示)
/* 本地渲染的Word文档 */
<div
ref={docxContainerRef}
className="w-full h-full"
@@ -479,15 +427,16 @@ export default function Documents() {
</div>
</div>
{/* 抽取内容区域 */}
<div className="w-80 bg-white p-4 rounded-lg shadow-md">
{/* 抽取内容区域 - 始终显示,但DOCX模式下不交互 */}
<div className="w-80 bg-white p-4 rounded-lg shadow-md mr-4 my-4 overflow-auto">
<h2 className="text-xl font-semibold mb-4"></h2>
<ul className="space-y-3">
{extractedContent.map((item) => (
<button
key={item.id}
onClick={() => handleContentClick(item)}
className="w-full text-left p-3 bg-gray-50 hover:bg-gray-100 cursor-pointer rounded-lg transition"
className={`w-full text-left p-3 ${fileType === "pdf" ? "bg-gray-50 hover:bg-gray-100 cursor-pointer" : "bg-gray-100"} rounded-lg transition`}
disabled={fileType === "docx"}
aria-label={`查看内容: ${item.text}`}
>
<p className="text-sm font-medium">{item.text}</p>
@@ -500,13 +449,14 @@ export default function Documents() {
{/* 添加自定义样式 */}
<style dangerouslySetInnerHTML={{
__html: `
/* 高亮显示样式 */
.docx-highlight {
background-color: #ffff00;
outline: 2px solid orange;
position: relative;
}
/* 找到的内容高亮 */
/* 找到的内容高亮样式 */
.docx-content-found {
background-color: rgba(255, 230, 0, 0.3);
outline: 1px solid orange;
-57
View File
@@ -1,57 +0,0 @@
// 简单测试docx-preview能否正常工作
import { renderAsync } from 'docx-preview';
async function testDocxPreview() {
try {
// DOCX文件URL
const fileUrl = "https://dev-xc-enroll.oss-cn-guangzhou.aliyuncs.com/uploads/7840-230620112939.docx";
console.log("正在获取文件:", fileUrl);
// 获取文件
const response = await fetch(fileUrl);
if (!response.ok) {
throw new Error(`网络请求失败: ${response.status} ${response.statusText}`);
}
console.log("文件下载成功,准备解析");
// 转换为ArrayBuffer
const buffer = await response.arrayBuffer();
console.log("获取到ArrayBuffer,长度:", buffer.byteLength);
// 创建容器
const container = document.getElementById('docx-container');
if (!container) {
throw new Error("找不到容器元素");
}
// 渲染文档
console.log("开始渲染文档...");
await renderAsync(buffer, container, null, {
className: "docx-viewer",
inWrapper: true,
ignoreWidth: false,
ignoreHeight: false
});
console.log("文档渲染成功");
} catch (error) {
console.error("文档渲染失败:", error);
}
}
// 页面加载完成后执行
window.addEventListener('DOMContentLoaded', () => {
const app = document.getElementById('app');
if (app) {
// 创建容器
app.innerHTML = `
<div style="width: 100%; height: 100vh; display: flex; flex-direction: column;">
<h1>测试 docx-preview</h1>
<div id="docx-container" style="flex: 1; border: 1px solid #ccc; overflow: auto;"></div>
</div>
`;
// 执行测试
testDocxPreview();
}
});
-221
View File
@@ -1,221 +0,0 @@
<!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>
<style>
body, html {
margin: 0;
padding: 0;
height: 100%;
font-family: Arial, sans-serif;
}
.container {
display: flex;
flex-direction: column;
height: 100vh;
padding: 20px;
box-sizing: border-box;
}
h1 {
margin-top: 0;
}
.tabs {
display: flex;
margin-bottom: 10px;
}
.tab {
padding: 10px 20px;
cursor: pointer;
background-color: #f1f1f1;
border: 1px solid #ccc;
border-bottom: none;
margin-right: 5px;
}
.tab.active {
background-color: #3498db;
color: white;
}
#preview-container {
flex: 1;
border: 1px solid #ccc;
overflow: hidden;
background-color: #f9f9f9;
position: relative;
}
.preview-content {
width: 100%;
height: 100%;
display: none;
}
.preview-content.active {
display: block;
}
iframe {
width: 100%;
height: 100%;
border: none;
}
#log {
height: 120px;
overflow: auto;
background-color: #2d2d2d;
color: #0f0;
padding: 10px;
font-family: monospace;
margin-top: 20px;
border-radius: 4px;
}
.download-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
}
.download-button {
display: inline-block;
padding: 10px 20px;
background-color: #3498db;
color: white;
text-decoration: none;
border-radius: 4px;
margin-top: 20px;
}
.loading {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(255,255,255,0.8);
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
.spinner {
border: 5px solid #f3f3f3;
border-top: 5px solid #3498db;
border-radius: 50%;
width: 50px;
height: 50px;
animation: spin 1s linear infinite;
margin-bottom: 20px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
</head>
<body>
<div class="container">
<h1>Word文档预览方案测试</h1>
<div class="tabs">
<div class="tab active" data-target="office">Office Online</div>
<div class="tab" data-target="google">Google Docs</div>
<div class="tab" data-target="download">下载查看</div>
</div>
<div id="preview-container">
<div id="loading" class="loading">
<div class="spinner"></div>
<div>加载中,请稍候...</div>
</div>
<div id="office-preview" class="preview-content active">
<iframe id="office-iframe" src=""></iframe>
</div>
<div id="google-preview" class="preview-content">
<iframe id="google-iframe" src=""></iframe>
</div>
<div id="download-preview" class="preview-content">
<div class="download-container">
<p>在线预览无法工作?</p>
<p>您可以下载文档在本地打开</p>
<a id="download-link" href="" class="download-button" download target="_blank">
下载文档
</a>
</div>
</div>
</div>
<div id="log">
<div>测试页面已加载,正在尝试不同的文档预览方案...</div>
</div>
</div>
<script>
// 配置
const docUrl = "https://dev-xc-enroll.oss-cn-guangzhou.aliyuncs.com/uploads/7840-230620112939.docx";
const officeOnlineUrl = `https://view.officeapps.live.com/op/embed.aspx?src=${encodeURIComponent(docUrl)}`;
const googleDocsUrl = `https://docs.google.com/viewer?url=${encodeURIComponent(docUrl)}&embedded=true`;
// 日志函数
function log(message) {
const logContainer = document.getElementById('log');
const logEntry = document.createElement('div');
logEntry.textContent = `${new Date().toLocaleTimeString()}: ${message}`;
logContainer.appendChild(logEntry);
logContainer.scrollTop = logContainer.scrollHeight;
console.log(message);
}
// 初始化函数
function init() {
// 设置iframe源
document.getElementById('office-iframe').src = officeOnlineUrl;
document.getElementById('google-iframe').src = googleDocsUrl;
document.getElementById('download-link').href = docUrl;
// 设置标签切换
const tabs = document.querySelectorAll('.tab');
tabs.forEach(tab => {
tab.addEventListener('click', function() {
// 移除所有标签的active类
tabs.forEach(t => t.classList.remove('active'));
// 添加当前标签的active类
this.classList.add('active');
// 隐藏所有内容
document.querySelectorAll('.preview-content').forEach(content => {
content.classList.remove('active');
});
// 显示对应内容
const target = this.getAttribute('data-target');
document.getElementById(`${target}-preview`).classList.add('active');
log(`切换到 ${target} 预览模式`);
});
});
// 监听iframe加载事件
document.getElementById('office-iframe').addEventListener('load', function() {
log('Office Online预览已加载');
document.getElementById('loading').style.display = 'none';
});
document.getElementById('google-iframe').addEventListener('load', function() {
log('Google Docs预览已加载');
});
log('页面初始化完成,正在加载Office Online预览...');
}
// 页面加载完成后执行初始化
window.addEventListener('DOMContentLoaded', init);
// 添加错误处理
window.addEventListener('error', function(event) {
log(`错误: ${event.message}`);
});
</script>
</body>
</html>