XLSX文档预览器
我将创建一个XLSX文档预览器的Demo,这个工具可以模拟从后端获取XLSX文件并展示其内容。
设计思路
- 创建一个直观的用户界面,包含文件上传和预览区域
- 实现XLSX文件解析功能(使用SheetJS库)
- 设计表格预览区域,支持多工作表切换
- 添加基本的数据操作功能(排序、搜索)
下面是完整的实现代码:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>XLSX文档预览器</title>
<script src="https://cdn.sheetjs.com/xlsx-0.19.3/package/dist/xlsx.full.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
min-height: 100vh;
padding: 20px;
color: #333;
}
.container {
max-width: 1200px;
margin: 0 auto;
background-color: white;
border-radius: 12px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
header {
background: linear-gradient(90deg, #4b6cb7 0%, #182848 100%);
color: white;
padding: 25px 30px;
text-align: center;
}
h1 {
font-size: 28px;
margin-bottom: 10px;
}
.subtitle {
font-size: 16px;
opacity: 0.8;
}
.main-content {
display: flex;
flex-direction: column;
padding: 25px;
}
.upload-section {
background-color: #f8f9fa;
border-radius: 8px;
padding: 20px;
margin-bottom: 25px;
border: 2px dashed #c3cfe2;
text-align: center;
transition: all 0.3s;
}
.upload-section:hover {
border-color: #4b6cb7;
background-color: #f0f4ff;
}
.upload-btn {
background: linear-gradient(90deg, #4b6cb7 0%, #182848 100%);
color: white;
border: none;
padding: 12px 25px;
border-radius: 6px;
font-size: 16px;
cursor: pointer;
transition: all 0.3s;
display: inline-flex;
align-items: center;
gap: 8px;
margin: 10px 0;
}
.upload-btn:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(75, 108, 183, 0.4);
}
.file-info {
margin-top: 15px;
font-size: 14px;
color: #666;
}
.preview-section {
display: none;
flex-direction: column;
gap: 20px;
}
.sheet-tabs {
display: flex;
gap: 10px;
flex-wrap: wrap;
border-bottom: 1px solid #e0e0e0;
padding-bottom: 15px;
}
.sheet-tab {
padding: 8px 16px;
background-color: #f1f1f1;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s;
}
.sheet-tab.active {
background-color: #4b6cb7;
color: white;
}
.sheet-tab:hover:not(.active) {
background-color: #e0e0e0;
}
.table-container {
overflow-x: auto;
border: 1px solid #e0e0e0;
border-radius: 8px;
max-height: 500px;
overflow-y: auto;
}
table {
width: 100%;
border-collapse: collapse;
}
th {
background-color: #f5f7fa;
position: sticky;
top: 0;
padding: 12px 15px;
text-align: left;
border-bottom: 2px solid #e0e0e0;
font-weight: 600;
}
td {
padding: 10px 15px;
border-bottom: 1px solid #e0e0e0;
}
tr:hover {
background-color: #f9f9f9;
}
.controls {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 15px;
}
.search-box {
display: flex;
align-items: center;
gap: 10px;
}
input[type="text"] {
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
width: 250px;
}
.btn {
padding: 8px 16px;
background-color: #f1f1f1;
border: 1px solid #ddd;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s;
}
.btn:hover {
background-color: #e0e0e0;
}
.btn-primary {
background-color: #4b6cb7;
color: white;
border-color: #4b6cb7;
}
.btn-primary:hover {
background-color: #3a5ca0;
}
.empty-state {
text-align: center;
padding: 40px 20px;
color: #666;
}
.empty-state i {
font-size: 48px;
margin-bottom: 15px;
color: #c3cfe2;
}
.stats {
display: flex;
gap: 20px;
margin-top: 10px;
flex-wrap: wrap;
}
.stat-card {
background-color: #f8f9fa;
padding: 15px;
border-radius: 8px;
flex: 1;
min-width: 150px;
text-align: center;
border-left: 4px solid #4b6cb7;
}
.stat-value {
font-size: 24px;
font-weight: bold;
color: #4b6cb7;
}
.stat-label {
font-size: 14px;
color: #666;
margin-top: 5px;
}
@media (max-width: 768px) {
.controls {
flex-direction: column;
align-items: stretch;
}
input[type="text"] {
width: 100%;
}
.stats {
flex-direction: column;
}
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>XLSX文档预览器</h1>
<p class="subtitle">上传并预览Excel文档内容</p>
</header>
<div class="main-content">
<div class="upload-section">
<h2>上传XLSX文件</h2>
<p>从您的设备上传Excel文件或使用示例文件进行预览</p>
<input type="file" id="fileInput" accept=".xlsx, .xls" style="display: none;">
<button class="upload-btn" id="uploadBtn">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14 2H6C5.46957 2 4.96086 2.21071 4.58579 2.58579C4.21071 2.96086 4 3.46957 4 4V20C4 20.5304 4.21071 21.0391 4.58579 21.4142C4.96086 21.7893 5.46957 22 6 22H18C18.5304 22 19.0391 21.7893 19.4142 21.4142C19.7893 21.0391 20 20.5304 20 20V8L14 2Z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M14 2V8H20" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M16 13H8" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M16 17H8" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10 9H9H8" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
选择文件
</button>
<button class="upload-btn" id="demoBtn">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 15C13.6569 15 15 13.6569 15 12C15 10.3431 13.6569 9 12 9C10.3431 9 9 10.3431 9 12C9 13.6569 10.3431 15 12 15Z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M19.4 15C19.2669 15.3031 19.1337 15.6062 19.0006 15.9094C18.5298 16.9031 18.0589 17.8969 17.5881 18.8906C17.2814 19.5469 16.9747 20.2031 16.668 20.8594C16.246 21.7812 15.121 22.0844 14.2923 21.5094C13.7461 21.1281 13.1999 20.7469 12.6537 20.3656C12.466 20.2406 12.2331 20.2406 12.0454 20.3656C11.2689 20.9094 10.4923 21.4531 9.71579 21.9969C8.79219 22.65 7.51913 22.3406 7.07427 21.3094C6.76757 20.6531 6.46086 19.9969 6.15416 19.3406C5.68332 18.3469 5.21248 17.3531 4.74164 16.3594C4.60852 16.0562 4.4754 15.7531 4.34228 15.45C3.88593 14.4 4.52546 13.2 5.647 13.2H7.2C7.75788 13.2 8.21178 12.7969 8.3449 12.2531C8.47802 11.7094 8.21178 11.1656 7.75788 10.875L6.34046 9.94687C5.61938 9.46875 5.35214 8.53125 5.75217 7.8C6.68579 5.99062 7.61941 4.18125 8.55302 2.37187C8.95306 1.64062 9.75239 1.2 10.605 1.2H13.395C14.2476 1.2 15.0469 1.64062 15.447 2.37187C16.3806 4.18125 17.3142 5.99062 18.2478 7.8C18.6479 8.53125 18.3806 9.46875 17.6595 9.94687L16.2421 10.875C15.7882 11.1656 15.522 11.7094 15.6551 12.2531C15.7882 12.7969 16.2421 13.2 16.8 13.2H18.353C19.4745 13.2 20.1141 14.4 19.6577 15.45L19.4 15Z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
使用示例文件
</button>
<div class="file-info" id="fileInfo">未选择文件</div>
</div>
<div class="preview-section" id="previewSection">
<div class="stats">
<div class="stat-card">
<div class="stat-value" id="sheetCount">0</div>
<div class="stat-label">工作表</div>
</div>
<div class="stat-card">
<div class="stat-value" id="rowCount">0</div>
<div class="stat-label">数据行数</div>
</div>
<div class="stat-card">
<div class="stat-value" id="colCount">0</div>
<div class="stat-label">数据列数</div>
</div>
</div>
<div class="sheet-tabs" id="sheetTabs"></div>
<div class="controls">
<div class="search-box">
<input type="text" id="searchInput" placeholder="搜索表格内容...">
<button class="btn" id="searchBtn">搜索</button>
</div>
<div>
<button class="btn" id="exportBtn">导出为CSV</button>
</div>
</div>
<div class="table-container">
<table id="dataTable">
<!-- 表格内容将通过JavaScript动态生成 -->
</table>
</div>
</div>
<div class="empty-state" id="emptyState">
<i>📊</i>
<h3>暂无数据</h3>
<p>请上传XLSX文件或使用示例文件开始预览</p>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const fileInput = document.getElementById('fileInput');
const uploadBtn = document.getElementById('uploadBtn');
const demoBtn = document.getElementById('demoBtn');
const fileInfo = document.getElementById('fileInfo');
const previewSection = document.getElementById('previewSection');
const emptyState = document.getElementById('emptyState');
const sheetTabs = document.getElementById('sheetTabs');
const dataTable = document.getElementById('dataTable');
const searchInput = document.getElementById('searchInput');
const searchBtn = document.getElementById('searchBtn');
const exportBtn = document.getElementById('exportBtn');
const sheetCount = document.getElementById('sheetCount');
const rowCount = document.getElementById('rowCount');
const colCount = document.getElementById('colCount');
let workbook = null;
let currentSheet = null;
// 上传按钮点击事件
uploadBtn.addEventListener('click', function() {
fileInput.click();
});
// 文件选择事件
fileInput.addEventListener('change', function(e) {
const file = e.target.files[0];
if (file) {
fileInfo.textContent = `已选择: ${file.name}`;
readFile(file);
}
});
// 示例文件按钮点击事件
demoBtn.addEventListener('click', function() {
fileInfo.textContent = '已加载: 示例数据.xlsx';
loadDemoData();
});
// 搜索按钮点击事件
searchBtn.addEventListener('click', function() {
filterTable();
});
// 导出按钮点击事件
exportBtn.addEventListener('click', function() {
exportToCSV();
});
// 读取文件
function readFile(file) {
const reader = new FileReader();
reader.onload = function(e) {
const data = new Uint8Array(e.target.result);
processExcelData(data);
};
reader.readAsArrayBuffer(file);
}
// 处理Excel数据
function processExcelData(data) {
try {
workbook = XLSX.read(data, { type: 'array' });
displayWorkbook(workbook);
} catch (error) {
alert('文件读取失败: ' + error.message);
}
}
// 显示工作簿
function displayWorkbook(wb) {
// 更新统计信息
sheetCount.textContent = wb.SheetNames.length;
// 创建工作表标签
sheetTabs.innerHTML = '';
wb.SheetNames.forEach((sheetName, index) => {
const tab = document.createElement('div');
tab.className = 'sheet-tab' + (index === 0 ? ' active' : '');
tab.textContent = sheetName;
tab.addEventListener('click', function() {
// 移除所有标签的active类
document.querySelectorAll('.sheet-tab').forEach(t => {
t.classList.remove('active');
});
// 为当前标签添加active类
tab.classList.add('active');
// 显示对应的工作表
displaySheet(sheetName);
});
sheetTabs.appendChild(tab);
});
// 显示第一个工作表
if (wb.SheetNames.length > 0) {
displaySheet(wb.SheetNames[0]);
}
// 显示预览区域,隐藏空状态
previewSection.style.display = 'flex';
emptyState.style.display = 'none';
}
// 显示工作表
function displaySheet(sheetName) {
currentSheet = sheetName;
const worksheet = workbook.Sheets[sheetName];
const html = XLSX.utils.sheet_to_html(worksheet, { id: "dataTable" });
// 更新表格
dataTable.innerHTML = html;
// 更新统计信息
const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
rowCount.textContent = jsonData.length - 1; // 减去标题行
colCount.textContent = jsonData.length > 0 ? jsonData[0].length : 0;
}
// 过滤表格
function filterTable() {
const searchTerm = searchInput.value.toLowerCase();
if (!searchTerm) return;
const rows = dataTable.getElementsByTagName('tr');
let matchCount = 0;
for (let i = 0; i < rows.length; i++) {
const cells = rows[i].getElementsByTagName('td');
let rowMatches = false;
for (let j = 0; j < cells.length; j++) {
const cellText = cells[j].textContent.toLowerCase();
if (cellText.includes(searchTerm)) {
rowMatches = true;
cells[j].style.backgroundColor = '#fff9c4';
} else {
cells[j].style.backgroundColor = '';
}
}
if (rowMatches) {
rows[i].style.display = '';
matchCount++;
} else {
rows[i].style.display = 'none';
}
}
// 如果第一行是标题,确保它始终显示
if (rows.length > 0) {
rows[0].style.display = '';
}
// 显示匹配结果
if (searchTerm) {
fileInfo.textContent = `找到 ${matchCount} 个匹配项`;
} else {
fileInfo.textContent = fileInfo.textContent.split(' - ')[0];
}
}
// 导出为CSV
function exportToCSV() {
if (!workbook || !currentSheet) {
alert('没有可导出的数据');
return;
}
const worksheet = workbook.Sheets[currentSheet];
const csv = XLSX.utils.sheet_to_csv(worksheet);
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
const link = document.createElement('a');
const url = URL.createObjectURL(blob);
link.setAttribute('href', url);
link.setAttribute('download', `${currentSheet}.csv`);
link.style.visibility = 'hidden';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
// 加载示例数据
function loadDemoData() {
// 创建示例工作簿数据
const demoWorkbook = XLSX.utils.book_new();
// 示例数据 - 员工信息
const employeeData = [
['ID', '姓名', '部门', '职位', '入职日期', '薪资'],
[1, '张三', '技术部', '前端工程师', '2020-03-15', 15000],
[2, '李四', '技术部', '后端工程师', '2019-07-22', 16000],
[3, '王五', '设计部', 'UI设计师', '2021-01-10', 12000],
[4, '赵六', '市场部', '市场专员', '2018-11-05', 10000],
[5, '钱七', '人事部', 'HR专员', '2020-09-18', 11000],
[6, '孙八', '技术部', '全栈工程师', '2019-05-30', 18000],
[7, '周九', '设计部', 'UX设计师', '2021-03-12', 13000],
[8, '吴十', '市场部', '市场经理', '2017-08-14', 15000]
];
const employeeSheet = XLSX.utils.aoa_to_sheet(employeeData);
XLSX.utils.book_append_sheet(demoWorkbook, employeeSheet, '员工信息');
// 示例数据 - 销售数据
const salesData = [
['产品', '一月', '二月', '三月', '季度总计'],
['产品A', 15000, 18000, 22000, 55000],
['产品B', 12000, 15000, 17000, 44000],
['产品C', 8000, 9500, 12000, 29500],
['产品D', 20000, 22000, 25000, 67000]
];
const salesSheet = XLSX.utils.aoa_to_sheet(salesData);
XLSX.utils.book_append_sheet(demoWorkbook, salesSheet, '销售数据');
// 显示示例工作簿
displayWorkbook(demoWorkbook);
}
});
</script>
</body>
</html>
功能说明
这个XLSX文档预览器具有以下功能:
- 文件上传:支持用户上传本地的XLSX/Excel文件
- 示例数据:提供示例数据,方便用户快速体验
- 多工作表支持:可以切换不同的工作表进行查看
- 数据统计:显示工作表数量、数据行数和列数
- 搜索功能:可以搜索表格内容并高亮显示
- 数据导出:支持将当前工作表导出为CSV格式
- 响应式设计:适配不同屏幕尺寸
使用方法
- 点击"选择文件"按钮上传本地XLSX文件
- 或点击"使用示例文件"加载示例数据
- 使用工作表标签切换不同工作表
- 使用搜索框查找特定内容
- 点击"导出为CSV"按钮导出当前工作表
这个预览器完全在浏览器端运行,不需要后端支持,使用了SheetJS库来解析Excel文件。您可以直接复制上面的代码到HTML文件中运行。