238 lines
5.0 KiB
Vue
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> |