前端上传 各类 文件 ,前端自己解析出来 生成界面 然后支持编辑(增强版 支持大多数文件格式的在线编辑)
前端上传完文件后,前端自己将内容解析出来 生成界面 然后支持编辑,支持大多数文件格式的在线编辑。
这个增强版的在线编辑工具支持以下主要文件格式类别:
- 文档格式:PDF、DOCX、TXT、Markdown
使用 PDF.js 处理 PDF 文件,支持分页浏览和文本编辑
使用 docx 库解析 Word 文档内容
集成 TinyMCE 富文本编辑器处理文本文件 - 表格格式:XLSX、XLS、CSV
基于 SheetJS 库实现表格的解析和编辑
支持多工作表切换和新增工作表
单元格内容实时编辑和保存 - 图像格式:JPG、PNG、GIF、WEBP
使用 Cropper.js 提供图像裁剪功能
支持图像旋转和基本编辑
保留原始图像格式导出 - 代码格式:HTML、CSS、JS、Python、Java 等
基于 CodeMirror 实现语法高亮
支持多种编程语言的编辑模式
提供代码自动补全和格式化
要注意的是,前端 PDF 编辑有一些局限性:
- 包含图片或复杂图形的 PDF 处理起来不友好,
- 纯图片 只能展示 不可以编辑
- ……
界面
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>全能文件在线编辑工具</title><script src="https://cdn.tailwindcss.com"></script><link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet"><!-- 文档处理库 --><script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.4.120/pdf.min.js"></script><script src="https://cdn.jsdelivr.net/npm/docx@8.5.0/build/index.min.js"></script><!-- 表格处理库 --><script src="https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js"></script><!-- 图像处理库 --><script src="https://cdn.jsdelivr.net/npm/cropperjs@1.6.1/dist/cropper.min.js"></script><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/cropperjs@1.6.1/dist/cropper.min.css"><!-- 代码编辑库 --><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/codemirror.min.css"><script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/codemirror.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/mode/javascript/javascript.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/mode/htmlmixed/htmlmixed.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/mode/css/css.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/mode/markdown/markdown.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/mode/python/python.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/mode/java/java.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/addon/edit/closetag.min.js"></script><!-- 富文本编辑器 --><script src="https://cdn.tiny.cloud/1/no-api-key/tinymce/6/tinymce.min.js"></script><script>tailwind.config = {theme: {extend: {colors: {primary: '#3B82F6',secondary: '#10B981',accent: '#8B5CF6',neutral: '#64748B',}}}}</script><style type="text/tailwindcss">@layer utilities {.content-auto {content-visibility: auto;}.editor-shadow {box-shadow: 0 2px 15px rgba(0, 0, 0, 0.05);}.transition-all-300 {transition: all 0.3s ease;}}</style>
</head>
<body class="bg-gray-50 text-gray-800"><!-- 顶部导航 --><header class="bg-white shadow-sm sticky top-0 z-50"><div class="container mx-auto px-4 py-3 flex justify-between items-center"><div class="flex items-center space-x-2"><i class="fa fa-file-text-o text-primary text-2xl"></i><h1 class="text-xl font-bold">全能文件在线编辑工具</h1></div><div class="flex items-center space-x-4"><div id="fileInfo" class="hidden items-center space-x-2"><i id="fileIcon" class="fa fa-file-o text-primary"></i><span id="currentFileName" class="truncate max-w-xs md:max-w-md"></span></div><button id="saveBtn" class="bg-secondary hover:bg-green-600 text-white px-4 py-2 rounded-md flex items-center disabled:opacity-50" disabled><i class="fa fa-save mr-2"></i>保存</button><button id="downloadBtn" class="bg-primary hover:bg-blue-600 text-white px-4 py-2 rounded-md flex items-center disabled:opacity-50" disabled><i class="fa fa-download mr-2"></i>下载</button></div></div></header><main class="container mx-auto px-4 py-6"><!-- 支持的格式说明 --><div class="bg-white rounded-lg p-4 mb-6 editor-shadow"><h3 class="font-semibold mb-2 flex items-center"><i class="fa fa-info-circle text-primary mr-2"></i>支持的文件格式</h3><div class="flex flex-wrap gap-2 text-sm"><span class="bg-blue-100 text-blue-800 px-2 py-1 rounded">文档: PDF, DOCX, TXT, MD</span><span class="bg-green-100 text-green-800 px-2 py-1 rounded">表格: XLSX, XLS, CSV</span><span class="bg-purple-100 text-purple-800 px-2 py-1 rounded">图像: JPG, PNG, GIF, WEBP</span><span class="bg-yellow-100 text-yellow-800 px-2 py-1 rounded">代码: HTML, CSS, JS, PY, JAVA</span></div></div><!-- 上传区域 --><section id="uploadSection" class="mb-6"><div class="bg-white rounded-xl p-6 text-center editor-shadow"><label for="fileInput" class="cursor-pointer"><div id="dropArea" class="border-2 border-dashed border-gray-300 rounded-lg p-8 transition-all-300 hover:border-primary hover:bg-blue-50"><i class="fa fa-cloud-upload text-5xl text-primary mb-4"></i><h2 class="text-xl font-semibold mb-2">上传文件</h2><p class="text-gray-500 mb-6">点击或拖拽文件到此处</p><button class="bg-primary hover:bg-blue-600 text-white px-6 py-2 rounded-md transition-all-300"><i class="fa fa-folder-open mr-2"></i>选择文件</button><input id="fileInput" type="file" class="hidden" accept=".pdf,.docx,.txt,.md,.xlsx,.xls,.csv,.jpg,.jpeg,.png,.gif,.webp,.html,.css,.js,.py,.java"></div></label></div></section><!-- 进度条 --><section id="progressSection" class="mb-6 hidden"><div class="bg-white rounded-xl p-4 editor-shadow"><div class="flex justify-between items-center mb-2"><h3 class="font-medium text-gray-700">处理中...</h3><span id="progressPercent">0%</span></div><div class="w-full bg-gray-200 rounded-full h-2"><div id="progressBar" class="bg-primary h-2 rounded-full" style="width: 0%"></div></div><p id="progressText" class="text-sm text-gray-500 mt-2">准备处理文件</p></div></section><!-- 编辑器容器 --><section id="editorContainer" class="hidden"><!-- 文件信息栏 --><div class="bg-white rounded-lg p-4 mb-6 flex flex-wrap justify-between items-center editor-shadow"><div class="flex items-center mb-2 sm:mb-0"><i id="editorFileIcon" class="fa fa-file-o text-primary mr-2"></i><span id="editorFileName" class="font-medium"></span><span id="editorFileSize" class="text-sm text-gray-500 ml-2"></span></div><div class="flex space-x-2"><button id="reloadBtn" class="bg-gray-100 hover:bg-gray-200 text-gray-700 px-3 py-1 rounded text-sm"><i class="fa fa-refresh mr-1"></i>重新加载</button><button id="backBtn" class="bg-gray-100 hover:bg-gray-200 text-gray-700 px-3 py-1 rounded text-sm"><i class="fa fa-arrow-left mr-1"></i>返回</button></div></div><!-- 文档编辑器 (PDF, DOCX) --><div id="documentEditor" class="hidden bg-white rounded-xl p-4 mb-6 editor-shadow"><div class="flex justify-between items-center mb-4"><h3 class="font-semibold">文档编辑器</h3><div class="flex space-x-2"><button id="docEditMode" class="bg-primary text-white px-3 py-1 rounded text-sm">编辑模式</button><button id="docPreviewMode" class="bg-gray-200 hover:bg-gray-300 text-gray-700 px-3 py-1 rounded text-sm">预览模式</button></div></div><div id="documentContent" class="min-h-[500px] border border-gray-200 rounded p-4"></div><!-- PDF分页控制 --><div id="pdfPagination" class="hidden flex justify-center mt-4"><div class="flex items-center space-x-4"><button id="prevPage" class="border border-gray-300 rounded px-3 py-1 hover:bg-gray-50 disabled:opacity-50" disabled><i class="fa fa-chevron-left"></i> 上一页</button><span id="pageInfo">第 1 页 / 共 0 页</span><button id="nextPage" class="border border-gray-300 rounded px-3 py-1 hover:bg-gray-50 disabled:opacity-50" disabled>下一页 <i class="fa fa-chevron-right"></i></button></div></div></div><!-- 表格编辑器 (XLSX, CSV) --><div id="spreadsheetEditor" class="hidden bg-white rounded-xl p-4 mb-6 editor-shadow"><div class="flex flex-wrap justify-between items-center mb-4 gap-2"><h3 class="font-semibold">表格编辑器</h3><div class="flex space-x-2"><select id="sheetSelector" class="border border-gray-300 rounded px-3 py-1 text-sm"><!-- 动态填充工作表 --></select><button id="addSheetBtn" class="bg-gray-100 hover:bg-gray-200 text-gray-700 px-3 py-1 rounded text-sm"><i class="fa fa-plus mr-1"></i>添加工作表</button></div></div><div class="overflow-x-auto"><table id="spreadsheetTable" class="min-w-full border-collapse"></table></div></div><!-- 图像编辑器 --><div id="imageEditor" class="hidden bg-white rounded-xl p-4 mb-6 editor-shadow"><div class="flex flex-wrap justify-between items-center mb-4 gap-2"><h3 class="font-semibold">图像编辑器</h3><div class="flex space-x-2"><button id="cropBtn" class="bg-primary hover:bg-blue-600 text-white px-3 py-1 rounded text-sm"><i class="fa fa-crop mr-1"></i>裁剪</button><button id="rotateLeftBtn" class="bg-gray-100 hover:bg-gray-200 text-gray-700 px-3 py-1 rounded text-sm"><i class="fa fa-rotate-left mr-1"></i>向左旋转</button><button id="rotateRightBtn" class="bg-gray-100 hover:bg-gray-200 text-gray-700 px-3 py-1 rounded text-sm"><i class="fa fa-rotate-right mr-1"></i>向右旋转</button><button id="applyImageChangesBtn" class="bg-secondary hover:bg-green-600 text-white px-3 py-1 rounded text-sm"><i class="fa fa-check mr-1"></i>应用更改</button></div></div><div class="border border-gray-200 rounded p-4 flex justify-center"><img id="editImage" src="" alt="编辑中的图像" class="max-w-full max-h-[500px]"></div><div id="cropperContainer" class="hidden mt-4 border border-gray-200 rounded p-4"><div class="relative" style="width: 100%;"><img id="cropperImage" src="" alt="裁剪中的图像"></div><div class="flex justify-center mt-4 space-x-2"><button id="confirmCropBtn" class="bg-primary hover:bg-blue-600 text-white px-3 py-1 rounded text-sm">确认裁剪</button><button id="cancelCropBtn" class="bg-gray-100 hover:bg-gray-200 text-gray-700 px-3 py-1 rounded text-sm">取消</button></div></div></div><!-- 代码/文本编辑器 --><div id="codeEditor" class="hidden bg-white rounded-xl p-4 mb-6 editor-shadow"><div class="flex justify-between items-center mb-4"><h3 class="font-semibold">代码/文本编辑器</h3><div><select id="codeModeSelector" class="border border-gray-300 rounded px-3 py-1 text-sm"><option value="text">纯文本</option><option value="htmlmixed">HTML</option><option value="css">CSS</option><option value="javascript">JavaScript</option><option value="python">Python</option><option value="java">Java</option><option value="markdown">Markdown</option></select></div></div><div id="codeContent" class="min-h-[500px]"></div></div><!-- 富文本编辑器 (TXT, MD) --><div id="richTextEditor" class="hidden bg-white rounded-xl p-4 mb-6 editor-shadow"><div class="flex justify-between items-center mb-4"><h3 class="font-semibold">富文本编辑器</h3><div class="flex space-x-2"><button id="richEditMode" class="bg-primary text-white px-3 py-1 rounded text-sm">编辑模式</button><button id="richPreviewMode" class="bg-gray-200 hover:bg-gray-300 text-gray-700 px-3 py-1 rounded text-sm">预览模式</button></div></div><div id="richTextContent" class="min-h-[500px] border border-gray-200 rounded p-4"></div><div id="richTextPreview" class="hidden min-h-[500px] border border-gray-200 rounded p-4"></div></div><!-- 不支持的格式 --><div id="unsupportedEditor" class="hidden bg-white rounded-xl p-6 text-center editor-shadow"><i class="fa fa-exclamation-triangle text-yellow-500 text-5xl mb-4"></i><h3 class="text-xl font-semibold mb-2">不支持的文件格式</h3><p class="text-gray-500 mb-6">抱歉,当前不支持此文件格式的在线编辑</p><button id="unsupportedBackBtn" class="bg-primary hover:bg-blue-600 text-white px-6 py-2 rounded-md">返回上传</button></div></section></main><footer class="bg-gray-800 text-white py-6 mt-12"><div class="container mx-auto px-4 text-center"><p>全能文件在线编辑工具 © 2025年7月16日 《半生过往》</p><p class="text-sm text-gray-400 mt-1">所有文件处理均在本地进行,保护您的隐私安全</p></div></footer><script>// 全局变量let currentFile = null;let fileType = '';let fileExtension = '';let fileContent = null;let codeMirrorEditor = null;let cropper = null;let pdfDoc = null;let currentPdfPage = 1;let totalPdfPages = 0;let workbook = null;let docxDocument = null;// DOM元素const fileInput = document.getElementById('fileInput');const dropArea = document.getElementById('dropArea');const uploadSection = document.getElementById('uploadSection');const progressSection = document.getElementById('progressSection');const progressBar = document.getElementById('progressBar');const progressPercent = document.getElementById('progressPercent');const progressText = document.getElementById('progressText');const editorContainer = document.getElementById('editorContainer');const fileInfo = document.getElementById('fileInfo');const fileIcon = document.getElementById('fileIcon');const currentFileName = document.getElementById('currentFileName');const editorFileName = document.getElementById('editorFileName');const editorFileSize = document.getElementById('editorFileSize');const editorFileIcon = document.getElementById('editorFileIcon');const saveBtn = document.getElementById('saveBtn');const downloadBtn = document.getElementById('downloadBtn');const reloadBtn = document.getElementById('reloadBtn');const backBtn = document.getElementById('backBtn');const unsupportedBackBtn = document.getElementById('unsupportedBackBtn');// 编辑器元素const documentEditor = document.getElementById('documentEditor');const spreadsheetEditor = document.getElementById('spreadsheetEditor');const imageEditor = document.getElementById('imageEditor');const codeEditor = document.getElementById('codeEditor');const richTextEditor = document.getElementById('richTextEditor');const unsupportedEditor = document.getElementById('unsupportedEditor');// 初始化PDF.jsconst pdfjsLib = window['pdfjs-dist/build/pdf'];pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.4.120/pdf.worker.min.js';// 事件监听fileInput.addEventListener('change', handleFileSelect);dropArea.addEventListener('dragover', handleDragOver);dropArea.addEventListener('dragleave', handleDragLeave);dropArea.addEventListener('drop', handleDrop);saveBtn.addEventListener('click', saveFile);downloadBtn.addEventListener('click', downloadFile);reloadBtn.addEventListener('click', reloadFile);backBtn.addEventListener('click', backToUpload);unsupportedBackBtn.addEventListener('click', backToUpload);// 文档编辑器事件document.getElementById('docEditMode').addEventListener('click', () => setDocMode(true));document.getElementById('docPreviewMode').addEventListener('click', () => setDocMode(false));document.getElementById('prevPage').addEventListener('click', goToPrevPage);document.getElementById('nextPage').addEventListener('click', goToNextPage);// 表格编辑器事件document.getElementById('sheetSelector').addEventListener('change', switchSheet);document.getElementById('addSheetBtn').addEventListener('click', addSheet);// 图像编辑器事件document.getElementById('cropBtn').addEventListener('click', startCrop);document.getElementById('confirmCropBtn').addEventListener('click', confirmCrop);document.getElementById('cancelCropBtn').addEventListener('click', cancelCrop);document.getElementById('rotateLeftBtn').addEventListener('click', () => rotateImage(-90));document.getElementById('rotateRightBtn').addEventListener('click', () => rotateImage(90));document.getElementById('applyImageChangesBtn').addEventListener('click', applyImageChanges);// 代码编辑器事件document.getElementById('codeModeSelector').addEventListener('change', changeCodeMode);// 富文本编辑器事件document.getElementById('richEditMode').addEventListener('click', () => setRichTextMode(true));document.getElementById('richPreviewMode').addEventListener('click', () => setRichTextMode(false));// 处理文件选择function handleFileSelect(e) {const file = e.target.files[0];if (file) {processFile(file);}}// 处理拖拽function handleDragOver(e) {e.preventDefault();dropArea.classList.add('border-primary', 'bg-blue-50');}function handleDragLeave(e) {e.preventDefault();dropArea.classList.remove('border-primary', 'bg-blue-50');}function handleDrop(e) {e.preventDefault();dropArea.classList.remove('border-primary', 'bg-blue-50');const file = e.dataTransfer.files[0];if (file) {const dataTransfer = new DataTransfer();dataTransfer.items.add(file);fileInput.files = dataTransfer.files;processFile(file);}}// 处理文件function processFile(file) {currentFile = file;fileExtension = getFileExtension(file.name).toLowerCase();fileType = getFileType(fileExtension);// 更新文件信息显示currentFileName.textContent = file.name;editorFileName.textContent = file.name;editorFileSize.textContent = formatFileSize(file.size);fileIcon.className = getFileIconClass(fileExtension);editorFileIcon.className = getFileIconClass(fileExtension);fileInfo.classList.remove('hidden');fileInfo.classList.add('flex');// 显示进度条uploadSection.classList.add('hidden');progressSection.classList.remove('hidden');updateProgress(10, '准备处理文件...');// 根据文件类型选择读取方式const reader = new FileReader();reader.onload = function(e) {fileContent = e.target.result;openEditorForFile();};try {if (fileType === 'image') {reader.readAsDataURL(file);} else if (['document', 'spreadsheet'].includes(fileType)) {reader.readAsArrayBuffer(file);} else {reader.readAsText(file);}} catch (error) {showError(`处理文件时出错: ${error.message}`);}}// 打开对应编辑器function openEditorForFile() {// 隐藏所有编辑器hideAllEditors();try {switch(fileType) {case 'document':if (fileExtension === 'pdf') {loadPdfDocument();} else if (fileExtension === 'docx') {loadDocxDocument();} else {loadRichTextDocument();}break;case 'spreadsheet':loadSpreadsheet();break;case 'image':loadImageEditor();break;case 'code':loadCodeEditor();break;case 'text':loadRichTextDocument();break;default:showUnsupportedFormat();}} catch (error) {showError(`打开编辑器失败: ${error.message}`);}}// 加载PDF文档function loadPdfDocument() {updateProgress(20, '解析PDF文件...');pdfjsLib.getDocument(new Uint8Array(fileContent)).promise.then(function(pdf) {pdfDoc = pdf;totalPdfPages = pdf.numPages;currentPdfPage = 1;updateProgress(40, '加载PDF内容...');document.getElementById('pdfPagination').classList.remove('hidden');updatePageInfo();renderPdfPage(currentPdfPage);updateProgress(100, 'PDF加载完成');setTimeout(() => {progressSection.classList.add('hidden');editorContainer.classList.remove('hidden');documentEditor.classList.remove('hidden');enableEditingButtons();}, 500);}).catch(error => {showError(`加载PDF失败: ${error.message}`);});}// 渲染PDF页面function renderPdfPage(pageNum) {pdfDoc.getPage(pageNum).then(function(page) {const progress = 40 + Math.round((pageNum / totalPdfPages) * 40);updateProgress(progress, `加载第 ${pageNum} 页`);page.getTextContent().then(function(content) {const container = document.getElementById('documentContent');container.innerHTML = '';const pageDiv = document.createElement('div');pageDiv.className = 'pdf-page p-4 border rounded';let lastY = null;let paragraph = document.createElement('p');paragraph.className = 'mb-4 leading-relaxed';paragraph.contentEditable = true;content.items.forEach(item => {if (lastY !== null && Math.abs(item.transform[5] - lastY) > 15) {pageDiv.appendChild(paragraph);paragraph = document.createElement('p');paragraph.className = 'mb-4 leading-relaxed';paragraph.contentEditable = true;}const span = document.createElement('span');span.textContent = item.str;paragraph.appendChild(span);lastY = item.transform[5];});if (paragraph.children.length > 0) {pageDiv.appendChild(paragraph);}container.appendChild(pageDiv);updatePageButtons();});});}// 加载DOCX文档function loadDocxDocument() {updateProgress(30, '解析DOCX文件...');// 使用docx库解析文档docx.Document.load(new Uint8Array(fileContent)).then(doc => {updateProgress(60, '转换文档内容...');const paragraphs = doc.paragraphs.map(p => {const text = p.text || '';return `<p class="mb-4" contentEditable="true">${text}</p>`;}).join('');document.getElementById('documentContent').innerHTML = paragraphs;document.getElementById('pdfPagination').classList.add('hidden');updateProgress(100, 'DOCX加载完成');setTimeout(() => {progressSection.classList.add('hidden');editorContainer.classList.remove('hidden');documentEditor.classList.remove('hidden');enableEditingButtons();}, 500);}).catch(error => {showError(`加载DOCX失败: ${error.message}`);});}// 加载富文本文档function loadRichTextDocument() {updateProgress(50, '准备富文本编辑器...');// 初始化TinyMCE富文本编辑器tinymce.init({selector: '#richTextContent',height: 500,plugins: 'advlist autolink lists link image charmap preview anchor',toolbar: 'undo redo | formatselect | bold italic | alignleft aligncenter alignright | bullist numlist outdent indent',setup: (editor) => {editor.on('init', () => {editor.setContent(fileContent);updateProgress(100, '文本加载完成');setTimeout(() => {progressSection.classList.add('hidden');editorContainer.classList.remove('hidden');richTextEditor.classList.remove('hidden');enableEditingButtons();}, 500);});}});}// 加载表格文件function loadSpreadsheet() {updateProgress(30, '解析表格文件...');try {workbook = XLSX.read(fileContent, { type: 'array' });const sheetSelector = document.getElementById('sheetSelector');sheetSelector.innerHTML = '';// 填充工作表选择器workbook.SheetNames.forEach(sheetName => {const option = document.createElement('option');option.value = sheetName;option.textContent = sheetName;sheetSelector.appendChild(option);});updateProgress(60, '渲染表格内容...');renderSheet(workbook.SheetNames[0]);updateProgress(100, '表格加载完成');setTimeout(() => {progressSection.classList.add('hidden');editorContainer.classList.remove('hidden');spreadsheetEditor.classList.remove('hidden');enableEditingButtons();}, 500);} catch (error) {showError(`加载表格失败: ${error.message}`);}}// 渲染工作表function renderSheet(sheetName) {const sheet = workbook.Sheets[sheetName];const table = document.getElementById('spreadsheetTable');table.innerHTML = '';// 转换为JSONconst jsonData = XLSX.utils.sheet_to_json(sheet, { header: 1 });// 创建表头const thead = document.createElement('thead');const headerRow = document.createElement('tr');headerRow.className = 'bg-gray-50';if (jsonData.length > 0) {// 表头单元格jsonData[0].forEach((cell, index) => {const th = document.createElement('th');th.className = 'border border-gray-200 px-3 py-2 text-sm font-medium';th.textContent = cell || `列 ${index + 1}`;th.contentEditable = true;th.addEventListener('input', (e) => {jsonData[0][index] = e.target.textContent;updateSheetData(sheetName, jsonData);});headerRow.appendChild(th);});thead.appendChild(headerRow);table.appendChild(thead);// 表格内容const tbody = document.createElement('tbody');// 数据行for (let i = 1; i < jsonData.length; i++) {const row = document.createElement('tr');row.className = i % 2 === 0 ? 'bg-white' : 'bg-gray-50';jsonData[i].forEach((cell, index) => {const td = document.createElement('td');td.className = 'border border-gray-200 px-3 py-2 text-sm';td.textContent = cell || '';td.contentEditable = true;td.addEventListener('input', (e) => {jsonData[i][index] = e.target.textContent;updateSheetData(sheetName, jsonData);});row.appendChild(td);});tbody.appendChild(row);}// 添加空行用于新增数据const emptyRow = document.createElement('tr');emptyRow.className = 'bg-gray-50';for (let i = 0; i < jsonData[0].length; i++) {const td = document.createElement('td');td.className = 'border border-gray-200 px-3 py-2 text-sm';td.contentEditable = true;td.addEventListener('input', (e) => {if (!jsonData[jsonData.length]) jsonData[jsonData.length] = [];jsonData[jsonData.length - 1][i] = e.target.textContent;updateSheetData(sheetName, jsonData);renderSheet(sheetName);});emptyRow.appendChild(td);}tbody.appendChild(emptyRow);table.appendChild(tbody);}}// 加载图像编辑器function loadImageEditor() {updateProgress(50, '准备图像编辑器...');const image = document.getElementById('editImage');const cropperImage = document.getElementById('cropperImage');image.src = fileContent;cropperImage.src = fileContent;image.onload = () => {updateProgress(100, '图像加载完成');setTimeout(() => {progressSection.classList.add('hidden');editorContainer.classList.remove('hidden');imageEditor.classList.remove('hidden');enableEditingButtons();}, 500);};}// 加载代码编辑器function loadCodeEditor() {updateProgress(30, '准备代码编辑器...');// 确定代码模式let mode = 'text';switch(fileExtension) {case 'html': mode = 'htmlmixed'; break;case 'css': mode = 'css'; break;case 'js': mode = 'javascript'; break;case 'py': mode = 'python'; break;case 'java': mode = 'java'; break;case 'md': mode = 'markdown'; break;}// 设置选择器document.getElementById('codeModeSelector').value = mode;// 初始化CodeMirrorcodeMirrorEditor = CodeMirror(document.getElementById('codeContent'), {value: fileContent,mode: mode,lineNumbers: true,theme: 'default',autoCloseTags: true,lineWrapping: true,indentUnit: 4});codeMirrorEditor.setSize('100%', '500px');updateProgress(100, '代码编辑器准备完成');setTimeout(() => {progressSection.classList.add('hidden');editorContainer.classList.remove('hidden');codeEditor.classList.remove('hidden');enableEditingButtons();}, 500);}// 显示不支持的格式function showUnsupportedFormat() {updateProgress(100, '不支持的文件格式');setTimeout(() => {progressSection.classList.add('hidden');editorContainer.classList.remove('hidden');unsupportedEditor.classList.remove('hidden');}, 500);}// 保存文件function saveFile() {// 更改按钮状态const originalSaveText = saveBtn.innerHTML;saveBtn.innerHTML = '<i class="fa fa-spinner fa-spin mr-2"></i>保存中...';saveBtn.disabled = true;try {let message = '';switch(fileType) {case 'document':if (fileExtension === 'pdf') {message = 'PDF更改已保存(前端暂存)';} else if (fileExtension === 'docx') {message = 'DOCX更改已保存(前端暂存)';} else {message = '文档更改已保存';}break;case 'spreadsheet':message = '表格更改已保存';break;case 'image':message = '图像更改已保存';break;case 'code':case 'text':message = '文本更改已保存';break;}// 恢复按钮状态setTimeout(() => {saveBtn.innerHTML = '<i class="fa fa-check mr-2"></i>已保存';setTimeout(() => {saveBtn.innerHTML = originalSaveText;saveBtn.disabled = false;}, 2000);alert(message);}, 800);} catch (error) {saveBtn.innerHTML = '<i class="fa fa-exclamation mr-2"></i>保存失败';setTimeout(() => {saveBtn.innerHTML = originalSaveText;saveBtn.disabled = false;}, 2000);alert(`保存失败: ${error.message}`);}}// 下载文件function downloadFile() {if (!currentFile) return;// 更改按钮状态const originalDownloadText = downloadBtn.innerHTML;downloadBtn.innerHTML = '<i class="fa fa-spinner fa-spin mr-2"></i>处理中...';downloadBtn.disabled = true;try {let blob, fileName = currentFile.name.replace(/\.[^/.]+$/, "") + "_edited." + fileExtension;switch(fileType) {case 'document':if (fileExtension === 'pdf') {// PDF下载 - 实际应用需要专用库blob = new Blob([fileContent], { type: 'application/pdf' });} else if (fileExtension === 'docx') {// DOCX下载需要额外处理blob = new Blob([fileContent], { type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' });} else {// 文本文件const content = tinymce.get('richTextContent').getContent();blob = new Blob([content], { type: 'text/plain' });}break;case 'spreadsheet':// 表格下载const excelData = XLSX.write(workbook, { bookType: fileExtension, type: 'array' });blob = new Blob([excelData], { type: 'application/octet-stream' });break;case 'image':// 图像下载const canvas = document.createElement('canvas');const img = document.getElementById('editImage');canvas.width = img.naturalWidth;canvas.height = img.naturalHeight;canvas.getContext('2d').drawImage(img, 0, 0);canvas.toBlob(blob => {saveBlob(blob, fileName);}, `image/${fileExtension}`);return; // 提前返回,因为toBlob是异步的case 'code':case 'text':// 代码/文本下载const textContent = codeMirrorEditor ? codeMirrorEditor.getValue() : tinymce.get('richTextContent').getContent();blob = new Blob([textContent], { type: 'text/plain' });break;}saveBlob(blob, fileName);// 恢复按钮状态setTimeout(() => {downloadBtn.innerHTML = '<i class="fa fa-check mr-2"></i>下载完成';setTimeout(() => {downloadBtn.innerHTML = originalDownloadText;downloadBtn.disabled = false;}, 2000);}, 800);} catch (error) {downloadBtn.innerHTML = '<i class="fa fa-exclamation mr-2"></i>下载失败';setTimeout(() => {downloadBtn.innerHTML = originalDownloadText;downloadBtn.disabled = false;}, 2000);alert(`下载失败: ${error.message}`);}}// 保存Blob为文件function saveBlob(blob, fileName) {const url = URL.createObjectURL(blob);const a = document.createElement('a');a.href = url;a.download = fileName;document.body.appendChild(a);a.click();// 清理setTimeout(() => {document.body.removeChild(a);URL.revokeObjectURL(url);}, 0);}// 重新加载文件function reloadFile() {if (currentFile) {// 销毁编辑器实例if (codeMirrorEditor) {codeMirrorEditor.toTextArea();codeMirrorEditor = null;}if (tinymce.get('richTextContent')) {tinymce.remove('richTextContent');}processFile(currentFile);}}// 返回上传界面function backToUpload() {// 销毁编辑器实例if (codeMirrorEditor) {codeMirrorEditor.toTextArea();codeMirrorEditor = null;}if (tinymce.get('richTextContent')) {tinymce.remove('richTextContent');}// 重置状态currentFile = null;fileContent = null;pdfDoc = null;workbook = null;// 重置UIfileInfo.classList.add('hidden');fileInfo.classList.remove('flex');editorContainer.classList.add('hidden');uploadSection.classList.remove('hidden');saveBtn.disabled = true;downloadBtn.disabled = true;fileInput.value = '';hideAllEditors();}// 图像编辑功能function startCrop() {document.getElementById('cropperContainer').classList.remove('hidden');// 初始化裁剪工具if (cropper) {cropper.destroy();}cropper = new Cropper(document.getElementById('cropperImage'), {aspectRatio: NaN,viewMode: 1,autoCropArea: 0.8});}function confirmCrop() {if (cropper) {const canvas = cropper.getCroppedCanvas();document.getElementById('editImage').src = canvas.toDataURL();cropper.destroy();cropper = null;document.getElementById('cropperContainer').classList.add('hidden');}}function cancelCrop() {if (cropper) {cropper.destroy();cropper = null;}document.getElementById('cropperContainer').classList.add('hidden');}function rotateImage(degrees) {const img = document.getElementById('editImage');const currentRotation = parseInt(img.style.transform.replace(/[^0-9.-]/g, '')) || 0;img.style.transform = `rotate(${currentRotation + degrees}deg)`;}function applyImageChanges() {// 这里可以添加更多图像处理逻辑alert('图像更改已应用');}// 表格编辑功能function switchSheet() {const sheetName = document.getElementById('sheetSelector').value;renderSheet(sheetName);}function addSheet() {let sheetNum = 1;let sheetName = `工作表${sheetNum}`;while (workbook.SheetNames.includes(sheetName)) {sheetNum++;sheetName = `工作表${sheetNum}`;}// 创建新工作表const newSheet = XLSX.utils.aoa_to_sheet([['列1', '列2', '列3', '列4', '列5'],['', '', '', '', '']]);// 添加到工作簿workbook.SheetNames.push(sheetName);workbook.Sheets[sheetName] = newSheet;// 更新选择器const option = document.createElement('option');option.value = sheetName;option.textContent = sheetName;option.selected = true;document.getElementById('sheetSelector').appendChild(option);// 渲染新工作表renderSheet(sheetName);}function updateSheetData(sheetName, data) {const newSheet = XLSX.utils.aoa_to_sheet(data);workbook.Sheets[sheetName] = newSheet;}// PDF导航功能function updatePageInfo() {document.getElementById('pageInfo').textContent = `第 ${currentPdfPage} 页 / 共 ${totalPdfPages} 页`;}function updatePageButtons() {document.getElementById('prevPage').disabled = currentPdfPage <= 1;document.getElementById('nextPage').disabled = currentPdfPage >= totalPdfPages;}function goToPrevPage() {if (currentPdfPage > 1) {currentPdfPage--;renderPdfPage(currentPdfPage);updatePageInfo();}}function goToNextPage() {if (currentPdfPage < totalPdfPages) {currentPdfPage++;renderPdfPage(currentPdfPage);updatePageInfo();}}// 模式切换功能function setDocMode(editMode) {const editBtn = document.getElementById('docEditMode');const previewBtn = document.getElementById('docPreviewMode');const content = document.getElementById('documentContent');if (editMode) {editBtn.classList.add('bg-primary', 'text-white');editBtn.classList.remove('bg-gray-200', 'text-gray-700');previewBtn.classList.add('bg-gray-200', 'text-gray-700');previewBtn.classList.remove('bg-primary', 'text-white');content.querySelectorAll('p').forEach(p => p.contentEditable = true);} else {previewBtn.classList.add('bg-primary', 'text-white');previewBtn.classList.remove('bg-gray-200', 'text-gray-700');editBtn.classList.add('bg-gray-200', 'text-gray-700');editBtn.classList.remove('bg-primary', 'text-white');content.querySelectorAll('p').forEach(p => p.contentEditable = false);}}function setRichTextMode(editMode) {const editBtn = document.getElementById('richEditMode');const previewBtn = document.getElementById('richPreviewMode');const editContent = document.getElementById('richTextContent');const previewContent = document.getElementById('richTextPreview');if (editMode) {editBtn.classList.add('bg-primary', 'text-white');editBtn.classList.remove('bg-gray-200', 'text-gray-700');previewBtn.classList.add('bg-gray-200', 'text-gray-700');previewBtn.classList.remove('bg-primary', 'text-white');editContent.classList.remove('hidden');previewContent.classList.add('hidden');} else {previewBtn.classList.add('bg-primary', 'text-white');previewBtn.classList.remove('bg-gray-200', 'text-gray-700');editBtn.classList.add('bg-gray-200', 'text-gray-700');editBtn.classList.remove('bg-primary', 'text-white');previewContent.innerHTML = tinymce.get('richTextContent').getContent();editContent.classList.add('hidden');previewContent.classList.remove('hidden');}}function changeCodeMode() {const mode = document.getElementById('codeModeSelector').value;if (codeMirrorEditor) {codeMirrorEditor.setOption('mode', mode);}}// 工具函数function getFileExtension(filename) {return filename.slice((filename.lastIndexOf(".") - 1 >>> 0) + 2);}function getFileType(extension) {const documentTypes = ['pdf', 'docx', 'doc'];const spreadsheetTypes = ['xlsx', 'xls', 'csv'];const imageTypes = ['jpg', 'jpeg', 'png', 'gif', 'webp'];const codeTypes = ['html', 'css', 'js', 'py', 'java', 'json', 'php', 'cpp', 'c'];const textTypes = ['txt', 'md', 'markdown'];if (documentTypes.includes(extension)) return 'document';if (spreadsheetTypes.includes(extension)) return 'spreadsheet';if (imageTypes.includes(extension)) return 'image';if (codeTypes.includes(extension)) return 'code';if (textTypes.includes(extension)) return 'text';return 'unknown';}function getFileIconClass(extension) {switch(extension) {case 'pdf': return 'fa fa-file-pdf-o text-red-500';case 'docx': return 'fa fa-file-word-o text-blue-500';case 'xlsx': case 'xls': return 'fa fa-file-excel-o text-green-500';case 'jpg': case 'jpeg': case 'png': case 'gif': case 'webp': return 'fa fa-file-image-o text-purple-500';case 'html': case 'css': case 'js': case 'py': case 'java': return 'fa fa-file-code-o text-yellow-500';case 'txt': case 'md': return 'fa fa-file-text-o text-gray-500';default: return 'fa fa-file-o text-gray-400';}}function formatFileSize(bytes) {if (bytes === 0) return '0 Bytes';const k = 1024;const sizes = ['Bytes', 'KB', 'MB', 'GB'];const i = Math.floor(Math.log(bytes) / Math.log(k));return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];}function updateProgress(percent, text) {progressBar.style.width = `${percent}%`;progressPercent.textContent = `${percent}%`;progressText.textContent = text;}function hideAllEditors() {documentEditor.classList.add('hidden');spreadsheetEditor.classList.add('hidden');imageEditor.classList.add('hidden');codeEditor.classList.add('hidden');richTextEditor.classList.add('hidden');unsupportedEditor.classList.add('hidden');}function enableEditingButtons() {saveBtn.disabled = false;downloadBtn.disabled = false;}function showError(message) {updateProgress(100, message);progressBar.classList.add('bg-red-500');progressBar.classList.remove('bg-primary');setTimeout(() => {progressSection.classList.add('hidden');progressBar.classList.remove('bg-red-500');progressBar.classList.add('bg-primary');uploadSection.classList.remove('hidden');}, 2000);}</script>
</body>
</html>