Files
xgy-oa/klp-ui/src/components/FilePreview/preview/xls/index.vue
砂糖 a19c4e4eaf feat(文件预览): 添加对PDF、Word和Excel文件的预览支持
添加新的文件预览组件,支持PDF、Word(docx)、Excel(xlsx/xls)文件类型预览
重构图片预览为独立组件,并添加相关依赖包
2026-04-10 08:42:59 +08:00

238 lines
5.0 KiB
Vue

<template>
<div class="xls-preview">
<!-- 加载状态 -->
<div v-if="loading" class="loading">
<div class="spinner"></div>
<p>正在加载Excel文件...</p>
</div>
<!-- 错误信息 -->
<div v-if="error" class="error">{{ error }}</div>
<!-- Excel内容展示 -->
<div v-if="workbook" class="workbook-container">
<!-- 工作表标签 -->
<div class="sheet-tabs">
<button v-for="(sheet, index) in workbook.SheetNames" :key="index"
:class="{ active: activeSheetIndex === index }" @click="activeSheetIndex = index">
{{ sheet }}
</button>
</div>
<!-- 表格内容区域 -->
<div class="sheet-content">
<table v-if="activeSheetData">
<tbody>
<tr v-for="(row, rowIndex) in activeSheetData" :key="rowIndex">
<td v-for="(cell, colIndex) in row" :key="colIndex" :class="{
'header-cell': rowIndex === 0,
'odd-row': rowIndex % 2 === 1
}">
{{ cell || '' }}
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</template>
<script>
import * as XLSX from 'xlsx';
export default {
name: "XlsPreview",
props: {
src: {
type: String,
required: true,
description: "Excel文件的URL或路径"
}
},
data () {
return {
workbook: null, // 解析后的Excel工作簿
activeSheetIndex: 0, // 当前激活的工作表索引
loading: false, // 加载状态
error: null // 错误信息
};
},
watch: {
src: {
immediate: true,
handler () {
this.loadAndParseExcel();
}
}
},
computed: {
// 获取当前激活的工作表数据
activeSheetData () {
if (!this.workbook || !this.workbook.SheetNames.length) return null;
const sheetName = this.workbook.SheetNames[this.activeSheetIndex];
const worksheet = this.workbook.Sheets[sheetName];
// 将工作表转换为二维数组
return XLSX.utils.sheet_to_json(worksheet, { header: 1 });
}
},
methods: {
/**
* 加载并解析Excel文件
*/
async loadAndParseExcel () {
this.loading = true;
this.error = null;
this.workbook = null;
try {
// 验证URL格式
if (!this.src || (this.src.startsWith('http') && !this.isValidUrl(this.src))) {
throw new Error('无效的文件路径');
}
// 加载文件
const response = await fetch(this.src);
if (!response.ok) {
throw new Error(`加载失败: ${response.status} ${response.statusText}`);
}
// 转换为ArrayBuffer
const arrayBuffer = await response.arrayBuffer();
// 解析Excel
this.workbook = XLSX.read(arrayBuffer, {
type: 'array',
cellDates: true, // 解析日期类型
cellText: false // 保持单元格原始类型
});
// 重置到第一个工作表
this.activeSheetIndex = 0;
} catch (err) {
this.error = err.message;
console.error('Excel处理错误:', err);
} finally {
this.loading = false;
}
},
/**
* 验证URL格式
*/
isValidUrl (url) {
try {
new URL(url);
return true;
} catch (e) {
return false;
}
}
}
};
</script>
<style scoped>
.xls-preview {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
border: 1px solid #e5e7eb;
border-radius: 6px;
overflow: hidden;
width: 100%;
height: 70vh;
}
/* 加载状态样式 */
.loading {
padding: 40px 20px;
text-align: center;
color: #6b7280;
}
.spinner {
width: 40px;
height: 40px;
margin: 0 auto 16px;
border: 4px solid #e5e7eb;
border-top-color: #3b82f6;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
/* 错误信息样式 */
.error {
padding: 20px;
color: #dc2626;
background-color: #fee2e2;
border-bottom: 1px solid #fecaca;
}
/* 工作表标签样式 */
.sheet-tabs {
display: flex;
background-color: #f9fafb;
border-bottom: 1px solid #e5e7eb;
overflow-x: auto;
white-space: nowrap;
}
.sheet-tabs button {
padding: 8px 16px;
border: none;
background: none;
font-size: 14px;
color: #4b5563;
cursor: pointer;
border-bottom: 2px solid transparent;
transition: all 0.2s;
}
.sheet-tabs button:hover:not(.active) {
background-color: #f3f4f6;
color: #1f2937;
}
.sheet-tabs button.active {
border-bottom-color: #3b82f6;
color: #3b82f6;
font-weight: 500;
}
/* 表格内容样式 */
.sheet-content {
overflow: auto;
max-height: 600px;
}
table {
width: 100%;
border-collapse: collapse;
min-width: max-content;
}
td {
padding: 8px 12px;
border: 1px solid #e5e7eb;
min-width: 80px;
font-size: 14px;
color: #1f2937;
}
.header-cell {
background-color: #f3f4f6;
font-weight: 500;
color: #111827;
}
.odd-row {
background-color: #f9fafb;
}
</style>