- Voltage - Forward (Vf) (Typ):
-
// selection-table.js - 完整实现
class SelectionTable {
constructor(options) {
// 默认配置
this.options = Object.assign({
apiUrl: '/product/selection/search',
configUrl: '/product/selection/config',
container: '#selection-table',
pageSize: 50,
debounceDelay: 300,
maxPrice: 10000,
showImages: true
}, options);
// 状态管理
this.state = {
keyword: '',
filters: {
attributes: {},
price_min: 0,
price_max: 0,
stock_min: 0
},
page: 1,
sort: 'relevance',
loading: false,
total: 0,
totalPages: 0,
selectedProducts: new Set() // 选中的产品ID
};
// DOM元素引用
this.elements = {};
// 防抖定时器
this.debounceTimer = null;
// 初始化
this.init();
}
/** 初始化选型表 */
async init() {
try {
// 加载配置
await this.loadConfig();
// 渲染UI
this.renderUI();
// 绑定事件
this.bindEvents();
// 执行初始搜索
await this.search();
} catch (error) {
console.error('选型表初始化失败:', error);
this.showError('选型表初始化失败,请刷新页面重试');
}
}
/** 加载配置信息 */
async loadConfig() {
try {
const response = await fetch(this.options.configUrl);
const result = await response.json();
if (result.code === 200) {
this.config = result.data;
// 设置默认价格范围
this.state.filters.price_max = this.config.max_price || this.options.maxPrice;
} else {
throw new Error(result.message || '配置加载失败');
}
} catch (error) {
console.error('加载配置失败:', error);
// 使用默认配置
this.config = {
attributes: [],
sort_options: [
{ value: 'relevance', label: '相关性' },
{ value: 'price_asc', label: '价格从低到高' },
{ value: 'price_desc', label: '价格从高到低' },
{ value: 'stock_desc', label: '库存从多到少' },
{ value: 'newest', label: '最新上架' }
],
page_sizes: [20, 50, 100],
defaults: {
page_size: 50,
sort: 'relevance'
},
max_price: this.options.maxPrice
};
this.state.filters.price_max = this.options.maxPrice;
}
}
/** 渲染主界面 */
renderUI() {
const container = document.querySelector(this.options.container);
if (!container) {
throw new Error(`容器元素 ${this.options.container} 未找到`);
}
container.innerHTML = this.generateHTML();
// 保存DOM元素引用
this.cacheElements();
// 渲染属性过滤器
this.renderAttributeFilters();
}
/** 生成HTML内容 */
generateHTML() {
return `
`;
}
/** 生成排序选项 */
generateSortOptions() {
return this.config.sort_options.map(option =>
``
).join('');
}
/** 生成分页大小选项 */
generatePageSizeOptions() {
return this.config.page_sizes.map(size =>
``
).join('');
}
/** 缓存DOM元素引用 */
cacheElements() {
this.elements = {
container: document.querySelector(this.options.container),
searchInput: document.querySelector(`${this.options.container} .search-input`),
searchBtn: document.querySelector(`${this.options.container} .search-btn`),
sortSelect: document.querySelector(`${this.options.container} .sort-select`),
pageSizeSelect: document.querySelector(`${this.options.container} .page-size-select`),
priceMin: document.querySelector(`${this.options.container} .price-min`),
priceMax: document.querySelector(`${this.options.container} .price-max`),
stockMin: document.querySelector(`${this.options.container} .stock-min`),
attributeFilters: document.querySelector(`${this.options.container} .attribute-filters`),
resetFilters: document.querySelector(`${this.options.container} .reset-filters`),
applyFilters: document.querySelector(`${this.options.container} .apply-filters`),
productsTbody: document.querySelector(`${this.options.container} #products-tbody`),
loadingState: document.querySelector(`${this.options.container} .loading-state`),
emptyState: document.querySelector(`${this.options.container} .empty-state`),
selectAll: document.querySelector(`${this.options.container} .select-all`),
selectedCount: document.querySelector(`${this.options.container} #selected-count`),
compareBtn: document.querySelector(`${this.options.container} .compare-products`),
exportBtn: document.querySelector(`${this.options.container} .export-selected`),
pagination: document.querySelector(`${this.options.container} #pagination`),
startRecord: document.querySelector(`${this.options.container} #start-record`),
endRecord: document.querySelector(`${this.options.container} #end-record`),
totalRecords: document.querySelector(`${this.options.container} #total-records`),
resetSearch: document.querySelector(`${this.options.container} .reset-search`)
};
}
/** 渲染属性过滤器 */
renderAttributeFilters() {
if (!this.config.attributes || !this.elements.attributeFilters) return;
const attributesHTML = this.config.attributes.map(attr => `
`).join('');
this.elements.attributeFilters.innerHTML = attributesHTML;
}
/** 绑定事件 */
bindEvents() {
// 搜索相关事件
this.elements.searchInput.addEventListener('input', this.debounce(() => {
this.state.keyword = this.elements.searchInput.value.trim();
this.state.page = 1;
this.search();
}, this.options.debounceDelay));
this.elements.searchBtn.addEventListener('click', () => {
this.state.keyword = this.elements.searchInput.value.trim();
this.state.page = 1;
this.search();
});
// 排序和分页大小变化
this.elements.sortSelect.addEventListener('change', () => {
this.state.sort = this.elements.sortSelect.value;
this.state.page = 1;
this.search();
});
this.elements.pageSizeSelect.addEventListener('change', () => {
this.options.pageSize = parseInt(this.elements.pageSizeSelect.value);
this.state.page = 1;
this.search();
});
// 过滤器事件
this.elements.applyFilters.addEventListener('click', () => {
this.updateFiltersFromUI();
this.state.page = 1;
this.search();
});
this.elements.resetFilters.addEventListener('click', () => {
this.resetFilters();
this.search();
});
// 选择相关事件
this.elements.selectAll.addEventListener('change', (e) => {
this.toggleSelectAll(e.target.checked);
});
// 重置搜索
this.elements.resetSearch?.addEventListener('click', () => {
this.resetFilters();
this.elements.searchInput.value = '';
this.state.keyword = '';
this.search();
});
// 键盘事件
this.elements.searchInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
this.state.keyword = this.elements.searchInput.value.trim();
this.state.page = 1;
this.search();
}
});
}
/** 防抖函数 */
debounce(func, delay) {
return (...args) => {
clearTimeout(this.debounceTimer);
this.debounceTimer = setTimeout(() => func.apply(this, args), delay);
};
}
/** 从UI更新过滤器状态 */
updateFiltersFromUI() {
// 价格范围
this.state.filters.price_min = parseInt(this.elements.priceMin.value) || 0;
this.state.filters.price_max = parseInt(this.elements.priceMax.value) || this.options.maxPrice;
// 库存
this.state.filters.stock_min = parseInt(this.elements.stockMin.value) || 0;
// 属性过滤器
const attributeElements = this.elements.attributeFilters.querySelectorAll('.attribute-filter');
this.state.filters.attributes = {};
attributeElements.forEach(attrEl => {
const attrId = attrEl.dataset.attrId;
const checkedValues = Array.from(attrEl.querySelectorAll('input:checked'))
.map(input => input.value);
if (checkedValues.length > 0) {
this.state.filters.attributes[attrId] = checkedValues;
}
});
}
/** 重置过滤器 */
resetFilters() {
this.state.filters = {
attributes: {},
price_min: 0,
price_max: this.config.max_price || this.options.maxPrice,
stock_min: 0
};
// 更新UI
this.elements.priceMin.value = '';
this.elements.priceMax.value = this.state.filters.price_max;
this.elements.stockMin.value = '';
// 重置属性复选框
const checkboxes = this.elements.attributeFilters.querySelectorAll('input[type="checkbox"]');
checkboxes.forEach(checkbox => checkbox.checked = false);
}
/** 执行搜索 */
async search() {
if (this.state.loading) return;
this.setStateLoading(true);
try {
const searchParams = this.buildSearchParams();
const response = await fetch(this.options.apiUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(searchParams)
});
const result = await response.json();
if (result.code === 200) {
this.renderProducts(result.data.products || []);
this.updatePagination(result.data.total || 0);
this.updateSelectionUI();
} else {
throw new Error(result.message || '搜索失败');
}
} catch (error) {
console.error('搜索失败:', error);
this.showError('搜索失败,请稍后重试');
this.renderProducts([]);
} finally {
this.setStateLoading(false);
}
}
/** 构建搜索参数 */
buildSearchParams() {
return {
keyword: this.state.keyword,
filters: this.state.filters,
page: this.state.page,
page_size: this.options.pageSize,
sort: this.state.sort
};
}
/** 设置加载状态 */
setStateLoading(loading) {
this.state.loading = loading;
if (loading) {
this.elements.loadingState.style.display = 'block';
this.elements.productsTbody.innerHTML = '';
this.elements.emptyState.style.display = 'none';
} else {
this.elements.loadingState.style.display = 'none';
}
}
/** 渲染产品列表 */
renderProducts(products) {
if (products.length === 0) {
this.elements.emptyState.style.display = 'block';
this.elements.productsTbody.innerHTML = '';
return;
}
this.elements.emptyState.style.display = 'none';
const productsHTML = products.map(product => `
|
${this.options.showImages && product.image ?
` ` :
'无图 '}
|
${this.escapeHtml(product.model)} |
${this.escapeHtml(product.brand)} |
${this.escapeHtml(product.category)} |
¥${product.price} |
${product.stock} |
${product.status === 1 ? '有货' : '缺货'}
|
|
`).join('');
this.elements.productsTbody.innerHTML = productsHTML;
// 绑定产品行事件
this.bindProductEvents();
}
/** 绑定产品行事件 */
bindProductEvents() {
// 复选框选择
const checkboxes = this.elements.productsTbody.querySelectorAll('.product-checkbox');
checkboxes.forEach(checkbox => {
checkbox.addEventListener('change', (e) => {
this.toggleProductSelection(e.target.value, e.target.checked);
});
});
// 详情按钮
const detailBtns = this.elements.productsTbody.querySelectorAll('.view-detail');
detailBtns.forEach(btn => {
btn.addEventListener('click', (e) => {
const productId = e.target.dataset.id;
this.viewProductDetail(productId);
});
});
// 加入询价按钮
const cartBtns = this.elements.productsTbody.querySelectorAll('.add-to-cart');
cartBtns.forEach(btn => {
btn.addEventListener('click', (e) => {
const productId = e.target.dataset.id;
this.addToInquiry(productId);
});
});
}
/** 切换产品选择状态 */
toggleProductSelection(productId, selected) {
if (selected) {
this.state.selectedProducts.add(productId);
} else {
this.state.selectedProducts.delete(productId);
this.elements.selectAll.checked = false;
}
this.updateSelectionUI();
// 更新行样式
const row = this.elements.productsTbody.querySelector(`tr[data-product-id="${productId}"]`);
if (row) {
row.classList.toggle('selected', selected);
}
}
/** 全选/取消全选 */
toggleSelectAll(selectAll) {
const checkboxes = this.elements.productsTbody.querySelectorAll('.product-checkbox');
checkboxes.forEach(checkbox => {
checkbox.checked = selectAll;
this.toggleProductSelection(checkbox.value, selectAll);
});
}
/** 更新选择状态UI */
updateSelectionUI() {
const selectedCount = this.state.selectedProducts.size;
this.elements.selectedCount.textContent = `已选择 ${selectedCount} 个产品`;
// 更新按钮状态
this.elements.compareBtn.disabled = selectedCount === 0;
this.elements.exportBtn.disabled = selectedCount === 0;
// 更新全选复选框状态
const totalProducts = this.elements.productsTbody.querySelectorAll('.product-checkbox').length;
this.elements.selectAll.checked = selectedCount > 0 && selectedCount === totalProducts;
this.elements.selectAll.indeterminate = selectedCount > 0 && selectedCount < totalProducts;
}
/** 更新分页 */
updatePagination(total) {
this.state.total = total;
this.state.totalPages = Math.ceil(total / this.options.pageSize);
// 更新记录信息
const startRecord = total === 0 ? 0 : (this.state.page - 1) * this.options.pageSize + 1;
const endRecord = Math.min(this.state.page * this.options.pageSize, total);
this.elements.startRecord.textContent = startRecord;
this.elements.endRecord.textContent = endRecord;
this.elements.totalRecords.textContent = total;
// 渲染分页按钮
this.renderPaginationButtons();
}
/** 渲染分页按钮 */
renderPaginationButtons() {
if (this.state.totalPages <= 1) {
this.elements.pagination.innerHTML = '';
return;
}
let paginationHTML = '';
const currentPage = this.state.page;
const totalPages = this.state.totalPages;
// 上一页
paginationHTML += `
上一页
`;
// 页码按钮
const showPages = 5; // 显示的页码数量
let startPage = Math.max(1, currentPage - Math.floor(showPages / 2));
let endPage = Math.min(totalPages, startPage + showPages - 1);
if (endPage - startPage + 1 < showPages) {
startPage = Math.max(1, endPage - showPages + 1);
}
for (let i = startPage; i <= endPage; i++) {
paginationHTML += `
${i}
`;
}
// 下一页
paginationHTML += `
下一页
`;
this.elements.pagination.innerHTML = paginationHTML;
// 绑定分页事件
this.bindPaginationEvents();
}
/** 绑定分页事件 */
bindPaginationEvents() {
const pageLinks = this.elements.pagination.querySelectorAll('.page-link');
pageLinks.forEach(link => {
link.addEventListener('click', (e) => {
e.preventDefault();
const page = parseInt(e.target.dataset.page);
if (page >= 1 && page <= this.state.totalPages && page !== this.state.page) {
this.state.page = page;
this.search();
}
});
});
}
/** 查看产品详情 */
viewProductDetail(productId) {
// 这里可以跳转到产品详情页或打开模态框
console.log('查看产品详情:', productId);
// window.open(`/product/detail/${productId}`, '_blank');
}
/** 加入询价单 */
addToInquiry(productId) {
// 这里可以实现加入询价单的逻辑
console.log('加入询价单:', productId);
this.showSuccess('产品已加入询价单');
}
/** 显示成功消息 */
showSuccess(message) {
// 可以使用Toast或其他UI组件
alert(`成功: ${message}`);
}
/** 显示错误消息 */
showError(message) {
// 可以使用Toast或其他UI组件
alert(`错误: ${message}`);
}
/** HTML转义 */
escapeHtml(unsafe) {
if (!unsafe) return '';
return unsafe
.replace(/&/g, "&")
.replace(//g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
/** 获取选中的产品ID */
getSelectedProducts() {
return Array.from(this.state.selectedProducts);
}
/** 清空选中状态 */
clearSelection() {
this.state.selectedProducts.clear();
this.updateSelectionUI();
// 更新所有复选框
const checkboxes = this.elements.productsTbody.querySelectorAll('.product-checkbox');
checkboxes.forEach(checkbox => checkbox.checked = false);
// 更新行样式
const rows = this.elements.productsTbody.querySelectorAll('tr');
rows.forEach(row => row.classList.remove('selected'));
}
/** 销毁实例 */
destroy() {
// 清理事件监听器和其他资源
clearTimeout(this.debounceTimer);
// 可以根据需要添加更多清理逻辑
}
}
// CSS样式(可以放在单独的CSS文件中)
const selectionTableStyles = `
.selection-table-container {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
.selection-toolbar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding: 15px;
background: #f8f9fa;
border-radius: 5px;
}
.search-section {
flex: 1;
max-width: 400px;
}
.filter-panel {
background: white;
border: 1px solid #dee2e6;
border-radius: 5px;
padding: 15px;
margin-bottom: 20px;
}
.filter-section {
margin-bottom: 15px;
}
.filter-section h6 {
margin-bottom: 8px;
color: #495057;
font-weight: 600;
}
.price-filter {
display: flex;
align-items: center;
gap: 10px;
}
.price-filter input {
width: 100px;
}
.attribute-values {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.attribute-value-checkbox {
display: flex;
align-items: center;
margin-right: 15px;
}
.attribute-value-checkbox input {
margin-right: 5px;
}
.filter-actions {
display: flex;
gap: 10px;
margin-top: 15px;
}
.products-table-section {
background: white;
border-radius: 5px;
}
.table-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px;
border-bottom: 1px solid #dee2e6;
}
.product-image {
width: 50px;
height: 50px;
object-fit: contain;
}
.no-image {
width: 50px;
height: 50px;
background: #f8f9fa;
display: flex;
align-items: center;
justify-content: center;
color: #6c757d;
font-size: 12px;
}
.status-badge {
padding: 3px 8px;
border-radius: 3px;
font-size: 12px;
}
.status-badge.in-stock {
background: #d4edda;
color: #155724;
}
.status-badge.out-of-stock {
background: #f8d7da;
color: #721c24;
}
.low-stock {
color: #dc3545;
font-weight: bold;
}
tr.selected {
background-color: #e3f2fd !important;
}
.loading-state, .empty-state {
text-align: center;
padding: 40px;
color: #6c757d;
}
.pagination-container {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px;
border-top: 1px solid #dee2e6;
}
@media (max-width: 768px) {
.selection-toolbar {
flex-direction: column;
gap: 15px;
}
.search-section {
max-width: 100%;
}
.table-header {
flex-direction: column;
gap: 10px;
align-items: flex-start;
}
.pagination-container {
flex-direction: column;
gap: 15px;
}
}
`;
// 将样式添加到页面
if (!document.querySelector('#selection-table-styles')) {
const styleEl = document.createElement('style');
styleEl.id = 'selection-table-styles';
styleEl.textContent = selectionTableStyles;
document.head.appendChild(styleEl);
}
// 导出类
if (typeof module !== 'undefined' && module.exports) {
module.exports = SelectionTable;
} else {
window.SelectionTable = SelectionTable;
}