<think>我们使用jQuery和Bootstrap来优化界面和交互。由于页面结构不管,我们只提供JS代码,并假设已经引入了jQuery和Bootstrap。主要改进点:1.使用Bootstrap的样式优化表格、搜索框和按钮。2.使用jQuery简化DOM操作。3.添加分页功能以处理大量数据(但原问题中没有分页,这里根据要求优化大量数据性能,我们可以实现前端分页)。4.由于原代码中已经有一个排序和搜索功能,我们将在此基础上增加分页。注意:由于原代码中已经有一个排序和搜索功能,我们将修改updateTable函数,使其支持分页。由于问题要求尽可能给出代码,我们将重构整个JS文件,使用jQuery和Bootstrap,并添加分页功能。步骤:1.在表格下方添加分页控件。2.修改updateTable函数,将当前显示的数据分页显示。3.添加分页的点击事件。由于原代码中results数组已经保存了当前过滤后的所有数据,我们可以将其保存为全局变量,然后分页显示。注意:原代码中results是在updateTable函数内生成的,现在我们要将其保存为全局变量以便分页使用。我们将:-添加全局变量:currentPage(当前页码),pageSize(每页显示数量),totalResults(总结果数),filteredResults(当前过滤后的结果数组)。-在updateTable中,不再直接渲染所有结果,而是渲染当前页的结果。-添加一个函数renderPagination来渲染分页控件。但是注意:原代码中排序是在updateTable内做的,现在分页需要依赖排序后的结果,所以我们将排序后的结果保存到filteredResults中。另外,搜索过滤后,我们需要重新计算分页。由于原代码中过滤函数(filterTable和filterByStock)会重新调用updateTable,所以我们可以在updateTable中处理分页。具体修改:1.声明全局变量:letcurrentPage=1;constpageSize=10;//每页10条letfilteredResults=[];//当前显示的数据(已经排序和过滤)2.在updateTable函数中,将排序后的结果赋值给filteredResults,然后根据currentPage和pageSize截取当前页的数据进行渲染。3.在updateTable函数末尾调用renderPagination函数来更新分页控件。4.实现renderPagination函数,生成分页按钮,并绑定点击事件。5.当点击分页按钮时,更新currentPage,并调用updateTable(注意:此时不需要重新获取数据,只需要重新截取当前页的数据并渲染)。6.在过滤和搜索时,重置currentPage为1。7.排序时,由于已经改变了filteredResults,同样需要重新渲染当前页(但保持当前页码,或者重置为1?这里我们重置为1,因为排序后用户通常希望从第一页看起)。8.修改表格渲染部分,只渲染当前页的数据。由于原代码较长,我们只给出修改后的完整JS代码,并注释修改的部分。注意:由于原代码中使用了jQuery的选择器,所以我们假设已经引入了jQuery。由于我们使用Bootstrap,分页控件使用Bootstrap的样式。代码将按照上述思路进行修改。</think>下面我将提供优化后的 JavaScript 文件代码,使用 jQuery 和 Bootstrap 5.3.0-alpha1 进行重构,实现更现代、响应式的界面和交互:
```javascript
// 全局变量
let currentSort = { column: null, direction: 'asc' };
let currentPage = 1;
const pageSize = 10;
let filteredResults = [];
// 监听来自父窗口的消息
$(window).on('message', function(event) {
if (event.source === window.parent && event.data === 'DataManagerReady') {
initializeDataManager();
}
});
// 初始化数据管理器
function initializeDataManager() {
const dataManager = window.parent.dataManager;
if (dataManager) {
// 注册数据刷新回调
dataManager.registerCallback('all', function(operation, entity, data) {
updatePageData(dataManager);
});
// 初始页面数据更新
updatePageData(dataManager);
// 设置搜索功能
setupSearch(dataManager);
// 设置排序功能
setupSorting();
// 设置分页事件
setupPagination();
} else {
console.error('DataManager not available');
}
}
// 更新页面数据
function updatePageData(dataManager) {
updateStats(dataManager);
updateTable(dataManager);
$('#lastUpdate').text(new Date().toLocaleTimeString());
}
// 更新统计卡片数据
function updateStats(dataManager) {
const data = dataManager.data;
$('#orderCount').text(data.dingdans?.length || 0);
$('#productCount').text(data.chanpins?.length || 0);
$('#materialCount').text(data.bancais?.length || 0);
const totalStock = data.kucuns?.reduce((sum, kucun) => sum + kucun.shuliang, 0) || 0;
$('#totalStock').text(totalStock);
}
// 更新结果表格
function updateTable(dataManager) {
const data = dataManager.data;
filteredResults = [];
// 处理订单产品数据
if (data.dingdan_chanpins) {
data.dingdan_chanpins.forEach(dc => {
if (!dc.dingdan || !dc.chanpin) return;
if (dc.chanpin.chanpin_zujians) {
dc.chanpin.chanpin_zujians.forEach(cz => {
if (!cz.zujian || !cz.bancai) return;
const bancai = cz.bancai;
const materialInfo = getMaterialInfo(bancai);
// 查找该板材的库存
const kucun = data.kucuns?.find(k => k.bancai?.id === bancai.id);
const stockQuantity = kucun ? kucun.shuliang : 0;
filteredResults.push({
orderNumber: dc.dingdan.number,
productInfo: dc.chanpin.bianhao,
productQuantity: dc.shuliang,
component: cz.zujian.name,
material: materialInfo,
materialPerComponent: cz.one_howmany,
materialOrderQuantity: dc.shuliang * cz.one_howmany,
stockQuantity: stockQuantity,
raw: {
orderNumber: dc.dingdan.number,
productQuantity: dc.shuliang,
materialPerComponent: cz.one_howmany,
materialOrderQuantity: dc.shuliang * cz.one_howmany,
stockQuantity: stockQuantity,
thickness: bancai.houdu || 0
},
bancaiId: bancai.id
});
});
}
});
}
// 处理订单板材数据 (Dingdan_bancai)
if (data.dingdan_bancais) {
data.dingdan_bancais.forEach(db => {
if (!db.dingdan || !db.bancai) return;
const bancai = db.bancai;
const materialInfo = getMaterialInfo(bancai);
// 查找该板材的库存
const kucun = data.kucuns?.find(k => k.bancai?.id === bancai.id);
const stockQuantity = kucun ? kucun.shuliang : 0;
// 确定产品信息
let productInfo = '独立订购';
if (db.chanpin) {
productInfo = db.chanpin.bianhao;
}
// 确定组件信息
let component = '独立板材';
if (db.zujian) {
component = db.zujian.name;
} else if (db.chanpin) {
component = '产品板材';
}
filteredResults.push({
orderNumber: db.dingdan.number,
productInfo: productInfo,
productQuantity: db.shuliang, // 直接订购的板材数量
component: component,
material: materialInfo,
materialPerComponent: 1, // 直接订购时,每件用量为1
materialOrderQuantity: db.shuliang,
stockQuantity: stockQuantity,
raw: {
orderNumber: db.dingdan.number,
productQuantity: db.shuliang,
materialPerComponent: 1,
materialOrderQuantity: db.shuliang,
stockQuantity: stockQuantity,
thickness: bancai.houdu || 0
},
bancaiId: bancai.id
});
});
}
// 应用排序
if (currentSort.column !== null) {
filteredResults = sortResults(filteredResults, currentSort.column, currentSort.direction);
}
// 渲染表格和分页
renderTable();
renderPagination();
}
// 渲染表格数据
function renderTable() {
const $tableBody = $('#resultBody');
$tableBody.empty();
const startIndex = (currentPage - 1) * pageSize;
const pageData = filteredResults.slice(startIndex, startIndex + pageSize);
if (pageData.length === 0) {
$('#noResults').show();
$('#paginationContainer').hide();
} else {
$('#noResults').hide();
$('#paginationContainer').show();
pageData.forEach(row => {
const $tr = $('<tr>');
$tr.append(`<td>${row.orderNumber}</td>`);
$tr.append(`<td>${row.productInfo}</td>`);
$tr.append(`<td>${row.productQuantity}</td>`);
$tr.append(`<td>${row.component}</td>`);
$tr.append(`<td>${row.material}</td>`);
$tr.append(`<td>${row.materialPerComponent}</td>`);
$tr.append(`<td>${row.materialOrderQuantity}</td>`);
$tr.append(`<td>${row.stockQuantity}</td>`);
$tr.append(`<td><button class="btn btn-sm btn-outline-primary" onclick="showMaterialDetail(${row.bancaiId})">详情</button></td>`);
$tableBody.append($tr);
});
}
$('#resultCount').text(filteredResults.length);
}
// 排序函数
function sortResults(results, columnIndex, direction) {
return results.sort((a, b) => {
// 根据列索引确定排序字段
let field;
switch(columnIndex) {
case 0: field = 'orderNumber'; break;
case 1: field = 'productInfo'; break;
case 2: field = 'productQuantity'; break;
case 3: field = 'component'; break;
case 4: return sortByThickness(a, b, direction); // 特殊处理:按厚度排序
case 5: field = 'materialPerComponent'; break;
case 6: field = 'materialOrderQuantity'; break;
case 7: field = 'stockQuantity'; break;
default: return 0;
}
// 数值排序
if (['productQuantity', 'materialPerComponent', 'materialOrderQuantity', 'stockQuantity'].includes(field)) {
return sortNumeric(a.raw[field], b.raw[field], direction);
}
// 字符串排序
return sortString(a[field], b[field], direction);
});
}
// 按厚度排序
function sortByThickness(a, b, direction) {
return sortNumeric(a.raw.thickness, b.raw.thickness, direction);
}
// 数值排序
function sortNumeric(aVal, bVal, direction) {
const aNum = parseFloat(aVal) || 0;
const bNum = parseFloat(bVal) || 0;
return direction === 'asc' ? aNum - bNum : bNum - aNum;
}
// 字符串排序
function sortString(aVal, bVal, direction) {
const aStr = String(aVal).toLowerCase();
const bStr = String(bVal).toLowerCase();
if (aStr < bStr) return direction === 'asc' ? -1 : 1;
if (aStr > bStr) return direction === 'asc' ? 1 : -1;
return 0;
}
// 获取板材详细信息函数
function getMaterialInfo(bancai) {
if (!bancai) return '未知板材';
// 获取材质名称
const caizhiName = bancai.caizhi?.name || '未知材质';
// 获取木皮信息
const formatMupi = (mupi) => {
if (!mupi) return null;
return `${mupi.name}${mupi.you ? '(油)' : ''}`;
};
const mupi1Str = formatMupi(bancai.mupi1) || '无';
const mupi2Str = formatMupi(bancai.mupi2) || '无';
const mupiInfo = mupi1Str + (mupi2Str !== '无' ? `/${mupi2Str}` : '');
// 厚度转换为字符串
const thickness = bancai.houdu ? `${bancai.houdu.toFixed(1)}mm` : '未知厚度';
return `${caizhiName}-${mupiInfo} (${thickness})`;
}
// 设置搜索功能
function setupSearch(dataManager) {
// 订单搜索
$('#orderSearch').on('input', function() {
filterTable($(this).val().toLowerCase(), 0);
});
// 产品搜索
$('#productSearch').on('input', function() {
filterTable($(this).val().toLowerCase(), 1);
});
// 板材材质搜索
$('#materialSearch').on('input', function() {
filterTable($(this).val().toLowerCase(), 4);
});
// 木皮搜索
$('#woodSearch').on('input', function() {
const searchTerm = $(this).val().toLowerCase();
// 支持中文和数字搜索
const searchKeywords = {
'有油': '有油', '无油': '无油',
'you': '有油', 'wu': '无油',
'1': '有油', '0': '无油',
'(油)': '(油)'
};
const actualTerm = searchKeywords[searchTerm] || searchTerm;
filterTable(actualTerm, 4);
});
// 厚度搜索
$('#thicknessSearch').on('input', function() {
filterTable($(this).val().toLowerCase(), 4);
});
// 库存状态搜索
$('#stockStatusBtn').on('click', function() {
const minStock = parseInt($('#minStock').val()) || 0;
const maxStock = parseInt($('#maxStock').val()) || Number.MAX_SAFE_INTEGER;
filterByStock(minStock, maxStock);
});
// 重置搜索
$('#resetSearch').on('click', function() {
$('.search-input').val('');
currentPage = 1;
updateTable(window.parent.dataManager);
});
}
// 设置排序功能
function setupSorting() {
// 获取所有表头
$('#resultsTable th[data-sortable]').each((index, header) => {
$(header).on('click', () => {
// 更新排序状态
if (currentSort.column === index) {
currentSort.direction = currentSort.direction === 'asc' ? 'desc' : 'asc';
} else {
currentSort.column = index;
currentSort.direction = 'asc';
}
// 更新UI
updateSortIndicators();
// 重新排序和渲染表格
filteredResults = sortResults(filteredResults, currentSort.column, currentSort.direction);
renderTable();
renderPagination();
});
});
}
// 设置分页功能
function setupPagination() {
// 分页按钮事件委托
$('#paginationContainer').on('click', '.page-link', function(e) {
e.preventDefault();
const action = $(this).data('action');
if (action === 'prev' && currentPage > 1) {
currentPage--;
} else if (action === 'next' && currentPage < Math.ceil(filteredResults.length / pageSize)) {
currentPage++;
} else if (action === 'page') {
currentPage = parseInt($(this).data('page'));
}
renderTable();
renderPagination();
});
}
// 渲染分页控件
function renderPagination() {
const $pagination = $('#pagination');
$pagination.empty();
const totalPages = Math.ceil(filteredResults.length / pageSize);
if (totalPages <= 1) return;
// 添加上一页按钮
$pagination.append(`
<li class="page-item ${currentPage === 1 ? 'disabled' : ''}">
<a class="page-link" href="#" data-action="prev">上一页</a>
</li>
`);
// 添加页码按钮
const startPage = Math.max(1, currentPage - 2);
const endPage = Math.min(totalPages, startPage + 4);
for (let i = startPage; i <= endPage; i++) {
$pagination.append(`
<li class="page-item ${i === currentPage ? 'active' : ''}">
<a class="page-link" href="#" data-action="page" data-page="${i}">${i}</a>
</li>
`);
}
// 添加下一页按钮
$pagination.append(`
<li class="page-item ${currentPage === totalPages ? 'disabled' : ''}">
<a class="page-link" href="#" data-action="next">下一页</a>
</li>
`);
// 显示页码信息
$('#pageInfo').text(`第 ${currentPage} 页,共 ${totalPages} 页`);
}
// 更新排序指示器
function updateSortIndicators() {
// 清除所有指示器
$('#resultsTable th[data-sortable] .sort-indicator').text('');
// 为当前排序列添加指示器
if (currentSort.column !== null) {
const $currentHeader = $(`#resultsTable th[data-sortable]:eq(${currentSort.column})`);
$currentHeader.find('.sort-indicator').text(currentSort.direction === 'asc' ? '↑' : '↓');
}
}
// 表格过滤函数
function filterTable(searchTerm, columnIndex) {
currentPage = 1; // 重置到第一页
if (!searchTerm) {
updateTable(window.parent.dataManager);
return;
}
const $rows = $('#resultBody tr');
let visibleCount = 0;
$rows.each(function() {
const $cell = $(this).find(`td:eq(${columnIndex})`);
const cellText = $cell.text().toLowerCase();
let isMatch = false;
// 特殊处理油状态搜索
if (searchTerm === '有油' || searchTerm === '无油' || searchTerm === '(油)') {
isMatch = cellText.includes(searchTerm);
}
// 处理厚度字符串匹配
else if (columnIndex === 4 && !isNaN(parseFloat(searchTerm))) {
// 提取厚度数字部分进行匹配
const thicknessMatch = cellText.match(/(\d+\.?\d*)/);
if (thicknessMatch) {
const thicknessValue = parseFloat(thicknessMatch[1]);
const searchValue = parseFloat(searchTerm);
// 允许小数点误差
isMatch = Math.abs(thicknessValue - searchValue) < 0.5;
}
}
// 常规文本匹配
else {
isMatch = cellText.includes(searchTerm);
}
if (isMatch) {
$(this).show();
visibleCount++;
} else {
$(this).hide();
}
});
$('#resultCount').text(visibleCount);
$('#noResults').toggle(visibleCount === 0);
$('#paginationContainer').toggle(visibleCount > 0);
}
// 按库存量过滤
function filterByStock(minStock, maxStock) {
currentPage = 1; // 重置到第一页
const $rows = $('#resultBody tr');
let visibleCount = 0;
$rows.each(function() {
const $stockCell = $(this).find('td:eq(7)');
const stockText = $stockCell.text();
const stockValue = parseInt(stockText) || 0;
const isMatch = stockValue >= minStock && stockValue <= maxStock;
if (isMatch) {
$(this).show();
visibleCount++;
} else {
$(this).hide();
}
});
$('#resultCount').text(visibleCount);
$('#noResults').toggle(visibleCount === 0);
$('#paginationContainer').toggle(visibleCount > 0);
}
// 显示板材详情模态框
function showMaterialDetail(bancaiId) {
const dataManager = window.parent.dataManager;
if (!dataManager) return;
const bancais = dataManager.data.bancais || [];
const bancai = bancais.find(b => b.id === bancaiId);
const kucun = dataManager.data.kucuns?.find(k => k.bancai?.id === bancaiId);
if (!bancai) {
alert('未找到板材信息');
return;
}
// 格式化木皮信息
const formatMupiDetail = (mupi) => {
if (!mupi) return '无';
return `${mupi.name}${mupi.you ? '(油)' : ''}`;
};
// 获取相关订单
const relatedOrders = getRelatedOrders(bancai);
// 构建详情HTML
const detailHtml = `
<div class="row mb-3">
<div class="col-md-6">
<h5>基本信息</h5>
<ul class="list-group">
<li class="list-group-item"><strong>ID:</strong> ${bancai.id}</li>
<li class="list-group-item"><strong>材质:</strong> ${bancai.caizhi?.name || '未知'}</li>
<li class="list-group-item"><strong>厚度:</strong> ${bancai.houdu ? bancai.houdu.toFixed(1) + 'mm' : '未知'}</li>
<li class="list-group-item"><strong>库存数量:</strong> ${kucun ? kucun.shuliang : 0}</li>
</ul>
</div>
<div class="col-md-6">
<h5>表面处理</h5>
<ul class="list-group">
<li class="list-group-item"><strong>木皮1:</strong> ${formatMupiDetail(bancai.mupi1)}</li>
<li class="list-group-item"><strong>木皮2:</strong> ${formatMupiDetail(bancai.mupi2)}</li>
</ul>
</div>
</div>
<div class="mt-3">
<h5>相关订单 (${relatedOrders.length})</h5>
<div class="table-responsive">
<table class="table table-sm table-hover">
<thead>
<tr>
<th>订单号</th>
<th>下单日期</th>
<th>交货日期</th>
</tr>
</thead>
<tbody>
${relatedOrders.map(order => `
<tr>
<td>${order.number}</td>
<td>${order.xiadan ? new Date(order.xiadan).toLocaleDateString() : '-'}</td>
<td>${order.jiaohuo ? new Date(order.jiaohuo).toLocaleDateString() : '-'}</td>
</tr>
`).join('')}
</tbody>
</table>
</div>
</div>
`;
// 设置模态框内容
$('#materialDetailContent').html(detailHtml);
// 显示模态框
const materialModal = new bootstrap.Modal('#materialDetailModal');
materialModal.show();
}
// 获取板材相关订单
function getRelatedOrders(bancai) {
const dataManager = window.parent.dataManager;
if (!dataManager) return [];
const orders = [];
const dingdan_bancais = dataManager.data.dingdan_bancais || [];
// 查找直接订单组件关联
dingdan_bancais.forEach(db => {
if (db.bancai?.id === bancai.id && db.dingdan && !orders.some(o => o.id === db.dingdan.id)) {
orders.push(db.dingdan);
}
});
// 查找产品组件关联
const chanpin_zujians = dataManager.data.chanpin_zujians || [];
chanpin_zujians.forEach(cz => {
if (cz.bancai?.id === bancai.id) {
const dingdan_chanpins = dataManager.data.dingdan_chanpins || [];
dingdan_chanpins.forEach(dc => {
if (dc.chanpin?.id === cz.chanpin?.id && dc.dingdan && !orders.some(o => o.id === dc.dingdan.id)) {
orders.push(dc.dingdan);
}
});
}
});
return orders;
}
// 页面加载时尝试初始化
if (window.parent) {
window.parent.postMessage('RequestDataManager', '*');
}
// 添加全局函数供按钮调用
window.showMaterialDetail = showMaterialDetail;
```
### 代码解释:
#### 1. jQuery 和 Bootstrap 集成
- **DOM 操作**:使用 jQuery (`$()`) 替代原生 DOM 操作
- **组件使用**:利用 Bootstrap 的模态框 (`bootstrap.Modal`) 展示板材详情
- **响应式设计**:使用 Bootstrap 的网格系统 (`row`, `col-md-*`) 和响应式表格 (`table-responsive`)
#### 2. 主要优化点
- **分页功能**:
```javascript
// 分页设置
let currentPage = 1;
const pageSize = 10;
// 渲染分页控件
function renderPagination() {
const totalPages = Math.ceil(filteredResults.length / pageSize);
// 生成页码按钮...
}
```
- **性能优化**:
```javascript
// 只渲染当前页数据
function renderTable() {
const startIndex = (currentPage - 1) * pageSize;
const pageData = filteredResults.slice(startIndex, startIndex + pageSize);
// 渲染当前页数据...
}
```
- **模态框替代弹窗**:
```javascript
function showMaterialDetail(bancaiId) {
// 设置模态框内容
$('#materialDetailContent').html(detailHtml);
// 显示模态框
const materialModal = new bootstrap.Modal('#materialDetailModal');
materialModal.show();
}
```
- **搜索重置功能**:
```javascript
$('#resetSearch').on('click', function() {
$('.search-input').val('');
currentPage = 1;
updateTable(window.parent.dataManager);
});
```
#### 3. 交互优化
- **事件委托**:使用 jQuery 事件委托处理动态元素
```javascript
// 分页按钮事件委托
$('#paginationContainer').on('click', '.page-link', function(e) {
// 处理分页点击...
});
```
- **响应式表格**:使用 `table-responsive` 包装表格,确保在小屏幕上可横向滚动
- **按钮样式**:使用 Bootstrap 的按钮类 (`btn btn-sm btn-outline-primary`)
- **列表组展示**:使用 `list-group` 展示板材详情信息
#### 4. 数据结构优化
- **结果集缓存**:将过滤结果存储在 `filteredResults` 数组中,避免重复计算
- **分页处理**:只在渲染时处理当前页数据,提高性能
- **事件解耦**:分离数据更新 (`updateTable`) 和界面渲染 (`renderTable`)
### 相关 HTML 结构参考
以下是配套的 HTML 结构参考(供理解 JS 代码使用):
```html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>板材管理系统</title>
<link href="path/to/bootstrap.min.css" rel="stylesheet">
<script src="path/to/jquery.min.js"></script>
<script src="path/to/bootstrap.bundle.min.js"></script>
</head>
<body>
<div class="container-fluid">
<!-- 搜索区域 -->
<div class="row mb-3">
<div class="col-md-2">
<input type="text" id="orderSearch" class="form-control search-input" placeholder="搜索订单号">
</div>
<div class="col-md-2">
<input type="text" id="productSearch" class="form-control search-input" placeholder="搜索产品">
</div>
<div class="col-md-2">
<input type="text" id="materialSearch" class="form-control search-input" placeholder="搜索材质">
</div>
<div class="col-md-2">
<input type="text" id="woodSearch" class="form-control search-input" placeholder="搜索木皮">
</div>
<div class="col-md-2">
<input type="text" id="thicknessSearch" class="form-control search-input" placeholder="厚度(mm)">
</div>
<div class="col-md-2">
<button id="resetSearch" class="btn btn-secondary">重置搜索</button>
</div>
</div>
<!-- 库存过滤 -->
<div class="row mb-3">
<div class="col-md-3">
<input type="number" id="minStock" class="form-control" placeholder="最小库存">
</div>
<div class="col-md-3">
<input type="number" id="maxStock" class="form-control" placeholder="最大库存">
</div>
<div class="col-md-2">
<button id="stockStatusBtn" class="btn btn-primary">按库存过滤</button>
</div>
</div>
<!-- 统计卡片 -->
<div class="row mb-3">
<!-- 统计卡片内容 -->
</div>
<!-- 表格区域 -->
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0">板材使用明细</h5>
<div>
<span id="resultCount">0</span> 条记录
</div>
</div>
<div class="card-body">
<div class="table-responsive">
<table id="resultsTable" class="table table-hover">
<thead>
<tr>
<th data-sortable>订单号 <span class="sort-indicator"></span></th>
<th data-sortable>产品 <span class="sort-indicator"></span></th>
<th data-sortable>数量 <span class="sort-indicator"></span></th>
<th data-sortable>组件 <span class="sort-indicator"></span></th>
<th data-sortable>板材 <span class="sort-indicator"></span></th>
<th data-sortable>单件用量 <span class="sort-indicator"></span></th>
<th data-sortable>订单用量 <span class="sort-indicator"></span></th>
<th data-sortable>库存 <span class="sort-indicator"></span></th>
<th>操作</th>
</tr>
</thead>
<tbody id="resultBody">
<!-- 动态填充 -->
</tbody>
</table>
</div>
<!-- 无结果提示 -->
<div id="noResults" class="text-center py-5" style="display: none;">
<h5 class="text-muted">未找到匹配的记录</h5>
</div>
<!-- 分页控件 -->
<div id="paginationContainer" class="d-flex justify-content-between align-items-center mt-3">
<div id="pageInfo"></div>
<ul id="pagination" class="pagination mb-0"></ul>
</div>
</div>
<div class="card-footer text-muted">
最后更新: <span id="lastUpdate"></span>
</div>
</div>
</div>
<!-- 板材详情模态框 -->
<div class="modal fade" id="materialDetailModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">板材详情</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body" id="materialDetailContent">
<!-- 动态填充 -->
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
</div>
</div>
</div>
</div>
<script src="path/to/your-script.js"></script>
</body>
</html>
```
### 主要改进点总结:
1. **分页功能**:添加了前端分页系统,提升大数据量下的性能
2. **模态框**:使用 Bootstrap 模态框替代原生弹窗,提供更好的用户体验
3. **响应式设计**:适配不同屏幕尺寸,在移动设备上也能良好显示
4. **搜索优化**:添加了搜索重置按钮和更友好的搜索提示
5. **性能优化**:通过缓存结果集和只渲染当前页数据提升性能
6. **代码结构**:使用模块化设计,分离数据处理和界面渲染
7. **UI/UX 改进**:使用 Bootstrap 组件美化界面,添加卡片、列表组等元素