功能增强: - Excel 文件预览(.xlsx, .xls) - Word 文件预览(.docx, .doc) - 使用动态导入减小初始包体积 技术实现: - xlsx 库(143KB gzipped) - mammoth 库(100KB gzipped) - 动态加载,仅在打开文件时导入 - HTML 表格渲染 Excel - HTML 内容渲染 Word 修改文件: - filePreviewHandlers.js - Office 预览处理器 - fileTypeHelpers.js - 添加 isExcelFile/isWordFile - FileEditorPanel.vue - 集成 Office 预览 UI - useFileEdit.ts - 添加 Office 文件类型判断 - index.vue - 更新配置和导入 - file-system.ts - 添加 Office 预览相关类型
222 lines
5.8 KiB
JavaScript
222 lines
5.8 KiB
JavaScript
/**
|
|
* Office 文件预览处理器
|
|
* 使用动态导入减小初始包体积
|
|
*/
|
|
|
|
// Excel 预览处理器
|
|
export async function previewExcel(file, container) {
|
|
try {
|
|
// 动态导入 xlsx 库
|
|
const XLSX = await import('xlsx')
|
|
|
|
// 读取文件
|
|
const arrayBuffer = await file.arrayBuffer()
|
|
const workbook = XLSX.read(arrayBuffer, { type: 'array' })
|
|
|
|
// 获取第一个工作表
|
|
const firstSheetName = workbook.SheetNames[0]
|
|
const worksheet = workbook.Sheets[firstSheetName]
|
|
|
|
// 转换为 HTML 表格
|
|
const html = XLSX.utils.sheet_to_html(worksheet, {
|
|
editable: false,
|
|
header: '',
|
|
footer: ''
|
|
})
|
|
|
|
// 渲染到容器
|
|
container.innerHTML = `
|
|
<div class="excel-preview">
|
|
<div class="excel-sheet-info">
|
|
<span class="sheet-name">📊 ${firstSheetName}</span>
|
|
<span class="sheet-count">${workbook.SheetNames.length} 个工作表</span>
|
|
</div>
|
|
<div class="excel-table-wrapper">
|
|
${html}
|
|
</div>
|
|
</div>
|
|
<style>
|
|
.excel-preview {
|
|
padding: 20px;
|
|
height: 100%;
|
|
overflow: auto;
|
|
}
|
|
.excel-sheet-info {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 8px 12px;
|
|
background: var(--color-fill-2);
|
|
border-radius: 6px;
|
|
margin-bottom: 16px;
|
|
}
|
|
.sheet-name {
|
|
font-weight: 600;
|
|
font-size: 14px;
|
|
}
|
|
.sheet-count {
|
|
font-size: 12px;
|
|
color: var(--color-text-3);
|
|
}
|
|
.excel-table-wrapper {
|
|
overflow: auto;
|
|
border: 1px solid var(--color-border-2);
|
|
border-radius: 6px;
|
|
}
|
|
.excel-table-wrapper table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
font-size: 13px;
|
|
}
|
|
.excel-table-wrapper td,
|
|
.excel-table-wrapper th {
|
|
border: 1px solid var(--color-border-2);
|
|
padding: 6px 10px;
|
|
text-align: left;
|
|
white-space: nowrap;
|
|
}
|
|
.excel-table-wrapper th {
|
|
background: var(--color-fill-2);
|
|
font-weight: 600;
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 1;
|
|
}
|
|
.excel-table-wrapper tr:hover {
|
|
background: var(--color-fill-1);
|
|
}
|
|
</style>
|
|
`
|
|
|
|
return { success: true }
|
|
} catch (error) {
|
|
console.error('Excel 预览失败:', error)
|
|
return { success: false, error: error.message }
|
|
}
|
|
}
|
|
|
|
// Word 预览处理器
|
|
export async function previewWord(file, container) {
|
|
try {
|
|
// 动态导入 mammoth 库
|
|
const mammoth = await import('mammoth')
|
|
|
|
// 读取文件并转换为 HTML
|
|
const arrayBuffer = await file.arrayBuffer()
|
|
const result = await mammoth.convertToHtml({ arrayBuffer })
|
|
|
|
// 渲染到容器
|
|
container.innerHTML = `
|
|
<div class="word-preview">
|
|
<div class="word-content">
|
|
${result.value}
|
|
</div>
|
|
${result.messages.length > 0 ? `
|
|
<div class="word-warnings">
|
|
<details>
|
|
<summary>转换警告 (${result.messages.length})</summary>
|
|
<ul>
|
|
${result.messages.map(msg => `<li>${msg.message}</li>`).join('')}
|
|
</ul>
|
|
</details>
|
|
</div>
|
|
` : ''}
|
|
</div>
|
|
<style>
|
|
.word-preview {
|
|
padding: 20px;
|
|
height: 100%;
|
|
overflow: auto;
|
|
}
|
|
.word-content {
|
|
line-height: 1.6;
|
|
color: var(--color-text-1);
|
|
}
|
|
.word-content h1,
|
|
.word-content h2,
|
|
.word-content h3,
|
|
.word-content h4,
|
|
.word-content h5,
|
|
.word-content h6 {
|
|
margin: 1em 0 0.5em;
|
|
font-weight: 600;
|
|
}
|
|
.word-content h1 { font-size: 2em; }
|
|
.word-content h2 { font-size: 1.5em; }
|
|
.word-content h3 { font-size: 1.25em; }
|
|
.word-content p {
|
|
margin: 0.5em 0;
|
|
}
|
|
.word-content ul,
|
|
.word-content ol {
|
|
margin: 0.5em 0;
|
|
padding-left: 2em;
|
|
}
|
|
.word-content table {
|
|
border-collapse: collapse;
|
|
width: 100%;
|
|
margin: 1em 0;
|
|
}
|
|
.word-content td,
|
|
.word-content th {
|
|
border: 1px solid var(--color-border-2);
|
|
padding: 6px 10px;
|
|
}
|
|
.word-content th {
|
|
background: var(--color-fill-2);
|
|
font-weight: 600;
|
|
}
|
|
.word-content img {
|
|
max-width: 100%;
|
|
height: auto;
|
|
}
|
|
.word-content a {
|
|
color: rgb(var(--primary-6));
|
|
text-decoration: none;
|
|
}
|
|
.word-content a:hover {
|
|
text-decoration: underline;
|
|
}
|
|
.word-warnings {
|
|
margin-top: 20px;
|
|
padding: 12px;
|
|
background: var(--color-warning-light-1);
|
|
border: 1px solid var(--color-warning-3);
|
|
border-radius: 6px;
|
|
font-size: 12px;
|
|
}
|
|
.word-warnings summary {
|
|
cursor: pointer;
|
|
font-weight: 600;
|
|
}
|
|
.word-warnings ul {
|
|
margin: 8px 0 0 20px;
|
|
}
|
|
</style>
|
|
`
|
|
|
|
return { success: true }
|
|
} catch (error) {
|
|
console.error('Word 预览失败:', error)
|
|
return { success: false, error: error.message }
|
|
}
|
|
}
|
|
|
|
// 判断是否为 Office 文件
|
|
export function isOfficeFile(fileName) {
|
|
const ext = fileName?.toLowerCase()?.split('.').pop()
|
|
return ['xlsx', 'xls', 'docx', 'doc'].includes(ext)
|
|
}
|
|
|
|
// 判断是否为 Excel 文件
|
|
export function isExcelFile(fileName) {
|
|
const ext = fileName?.toLowerCase()?.split('.').pop()
|
|
return ['xlsx', 'xls'].includes(ext)
|
|
}
|
|
|
|
// 判断是否为 Word 文件
|
|
export function isWordFile(fileName) {
|
|
const ext = fileName?.toLowerCase()?.split('.').pop()
|
|
return ['docx', 'doc'].includes(ext)
|
|
}
|