This commit is contained in:
sjk
2025-11-28 15:18:10 +08:00
parent ad4a600af9
commit 5683f35942
188 changed files with 53680 additions and 1062 deletions

View File

@@ -0,0 +1,614 @@
// 商品列表页面逻辑 - 使用真实API
// 商品列表管理器
const productList = {
// 当前筛选和排序状态
filters: {
availability: [],
priceMin: null,
priceMax: null,
category: []
},
sortBy: 'featured',
currentPage: 1,
itemsPerPage: 20,
// 所有商品数据
totalCount: 0,
// 分类数据
categories: [],
// 初始化
init() {
// 绑定事件
this.bindEvents();
// 加载分类列表
this.loadCategories();
// 加载商品列表
this.loadProducts();
// 加载购物车数量
this.loadCartCount();
},
// 加载分类列表
loadCategories() {
console.log('加载分类列表');
API.get('/products/categories', { platform: 'web' })
.then(data => {
console.log('分类数据:', data);
this.categories = data || [];
this.renderCategories();
})
.catch(error => {
console.error('加载分类失败:', error);
// 失败时使用空数组,不影响页面其他功能
this.categories = [];
});
},
// 渲染分类筛选器(京东风格 - 悬浮窗式)
renderCategories() {
if (this.categories.length === 0) {
console.warn('分类数据为空');
return;
}
console.log('开始渲染分类,总数:', this.categories.length);
const lang = i18n.currentLang;
const $categoryContainer = $('.filter-group:has(h4[data-i18n="filter_category"]) .filter-options');
let categoryHtml = '';
// 遍历一级分类
this.categories.forEach((category, index) => {
console.log(`渲染一级分类 [${index}]:`, category.id, category.name);
const categoryName = this.getCategoryName(category, lang);
const isSelected = this.filters.category.includes(category.id.toString());
// 一级分类
categoryHtml += `
<div class="category-group">
<div class="category-parent ${isSelected ? 'active' : ''}" data-category-id="${category.id}">
<span class="category-name">${categoryName}</span>
<span class="category-arrow"></span>
</div>
`;
// 如果有二级分类,创建悬浮窗
if (category.children && category.children.length > 0) {
console.log(` 二级分类数量: ${category.children.length}`);
categoryHtml += '<div class="category-popup">';
categoryHtml += `<div class="category-popup-title">${categoryName}</div>`;
categoryHtml += '<div class="category-popup-content">';
category.children.forEach((child, childIndex) => {
console.log(` 渲染二级分类 [${childIndex}]:`, child.id, child.name);
const childName = this.getCategoryName(child, lang);
const isChildSelected = this.filters.category.includes(child.id.toString());
categoryHtml += `
<span class="category-tag ${isChildSelected ? 'active' : ''}" data-category-id="${child.id}" data-parent="${category.id}">
${childName}
</span>
`;
});
categoryHtml += '</div></div>';
}
categoryHtml += '</div>';
});
console.log('分类 HTML 生成完成');
$categoryContainer.html(categoryHtml);
// 绑定分类点击事件
this.bindCategoryClick();
// 验证渲染结果
console.log('渲染后的分类元素数量:', $('.category-parent').length);
$('.category-parent').each(function(idx) {
const id = $(this).data('category-id');
const name = $(this).find('.category-name').text();
console.log(` 分类 [${idx}]: ID=${id}, Name=${name}`);
});
},
// 绑定分类点击事件
bindCategoryClick() {
let hideTimeout = null;
// 鼠标进入分类组时,隐藏所有其他悬浮窗,只显示当前的
$('.category-group').on('mouseenter', function() {
clearTimeout(hideTimeout);
// 隐藏所有悬浮窗
$('.category-popup').removeClass('show');
// 计算当前分类的位置
const $parent = $(this).find('.category-parent');
const parentRect = $parent[0].getBoundingClientRect();
// 只显示当前的悬浫窗
const $popup = $(this).find('.category-popup');
// 设置悬浮窗位置(在一级分类右侧)
$popup.css({
left: parentRect.right + 12 + 'px', // 分类右侧 + 12px 间隙
top: parentRect.top + 'px' // 与分类顶部对齐
});
$popup.addClass('show');
});
// 鼠标离开分类组时,延迟隐藏悬浮窗
$('.category-group').on('mouseleave', function(e) {
const $popup = $(this).find('.category-popup');
hideTimeout = setTimeout(() => {
// 检查鼠标是否在悬浮窗内
if (!$popup.is(':hover')) {
$popup.removeClass('show');
}
}, 150); // 150ms 延迟
});
// 鼠标进入悬浮窗时,取消隐藏
$(document).on('mouseenter', '.category-popup', function() {
clearTimeout(hideTimeout);
$(this).addClass('show');
});
// 鼠标离开悬浮窗时,隐藏
$(document).on('mouseleave', '.category-popup', function() {
$(this).removeClass('show');
});
// 点击一级分类进行筛选(选择该分类下所有商品)
$(document).on('click', '.category-parent', function(e) {
e.stopPropagation();
e.preventDefault();
const categoryId = $(this).data('category-id');
console.log('点击一级分类ID:', categoryId);
if (!categoryId) {
console.error('分类 ID 不存在');
return;
}
const categoryIdStr = categoryId.toString();
const isActive = $(this).hasClass('active');
console.log('当前分类状态:', isActive ? '已选中' : '未选中');
// 移除所有选中状态
$('.category-tag').removeClass('active');
$('.category-parent').removeClass('active');
if (isActive) {
// 如果已选中,则取消选中(查看全部)
productList.filters.category = [];
console.log('取消选中,查看全部商品');
} else {
// 选中当前一级分类
$(this).addClass('active');
productList.filters.category = [categoryIdStr];
console.log('选中一级分类:', categoryIdStr);
}
// 重新加载商品
productList.currentPage = 1;
productList.loadProducts();
});
// 点击二级分类进行筛选
$(document).on('click', '.category-tag', function(e) {
e.stopPropagation();
e.preventDefault();
const categoryId = $(this).data('category-id');
console.log('点击二级分类ID:', categoryId);
if (!categoryId) {
console.error('分类 ID 不存在');
return;
}
const categoryIdStr = categoryId.toString();
const isActive = $(this).hasClass('active');
console.log('当前分类状态:', isActive ? '已选中' : '未选中');
// 移除所有选中状态
$('.category-tag').removeClass('active');
$('.category-parent').removeClass('active');
if (isActive) {
// 如果已选中,则取消选中(查看全部)
productList.filters.category = [];
console.log('取消选中,查看全部商品');
} else {
// 选中当前分类
$(this).addClass('active');
productList.filters.category = [categoryIdStr];
console.log('选中二级分类:', categoryIdStr);
}
// 隐藏悬浮窗
$('.category-popup').removeClass('show');
// 重新加载商品
productList.currentPage = 1;
productList.loadProducts();
});
},
// 获取分类名称(支持多语言)
getCategoryName(category, lang) {
// 如果有多语言数据
if (category.name_i18n && category.name_i18n[lang]) {
return category.name_i18n[lang];
}
// 降级使用默认名称
return category.name || '未知分类';
},
// 加载商品列表
loadProducts() {
const params = {
page: this.currentPage,
page_size: this.itemsPerPage
};
// 添加价格筛选参数(元转分)
if (this.filters.priceMin !== null && this.filters.priceMin > 0) {
params.min_price = Math.round(this.filters.priceMin * 100);
}
if (this.filters.priceMax !== null && this.filters.priceMax > 0) {
params.max_price = Math.round(this.filters.priceMax * 100);
}
// 添加分类筛选参数
if (this.filters.category && this.filters.category.length > 0) {
// 如果后端支持多个分类,使用数组
params.category_ids = this.filters.category.join(',');
}
// 添加库存筛选参数
if (this.filters.availability && this.filters.availability.length > 0) {
if (this.filters.availability.includes('in_stock') && !this.filters.availability.includes('out_of_stock')) {
params.in_stock = true;
} else if (!this.filters.availability.includes('in_stock') && this.filters.availability.includes('out_of_stock')) {
params.in_stock = false;
}
// 如果同时选中或都未选中,不添加该参数
}
// 添加排序参数
if (this.sortBy === 'price_asc') {
params.sort = 'price';
params.sortType = 0;
} else if (this.sortBy === 'price_desc') {
params.sort = 'price';
params.sortType = 1;
} else if (this.sortBy === 'date_new') {
params.sort = 'created_at';
params.sortType = 1;
}
console.log('加载商品列表,参数:', params);
// 显示加载状态
$('#productGrid').html('<div class="loading">加载中...</div>');
// 调用API
API.get('/products', params)
.then(data => {
console.log('商品列表数据:', data);
const products = data.list || [];
this.totalCount = data.total || 0;
this.renderProducts(products);
this.updateProductCount();
this.renderPagination();
})
.catch(error => {
console.error('加载商品列表失败:', error);
$('#productGrid').html('<div class="error">加载失败,请稍后重试</div>');
Toast.error(error.message || '加载商品失败');
});
},
// 渲染商品列表
renderProducts(products) {
const lang = i18n.currentLang;
if (products.length === 0) {
// 美化的空状态
$('#productGrid').html(`
<div class="empty-state">
<div class="empty-icon">
<svg width="120" height="120" viewBox="0 0 120 120" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="60" cy="60" r="50" fill="#F5F5F5"/>
<path d="M40 50 L50 60 L40 70 M80 50 L70 60 L80 70 M55 45 L65 75" stroke="#CCCCCC" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
<h3 class="empty-title">暂无商品</h3>
<p class="empty-desc">请尝试调整筛选条件或清除筛选</p>
<button class="btn btn-primary empty-btn" onclick="productList.clearFilters()">清除筛选</button>
</div>
`);
return;
}
const productsHtml = products.map(product => {
const productName = product.name || '未知商品';
const productImage = product.main_image || 'https://picsum.photos/400/400?random=default';
const price = PriceUtils.fenToYuan(product.price || 0);
const originalPrice = product.orig_price ? PriceUtils.fenToYuan(product.orig_price) : null;
const isInStock = product.stock > 0;
// 计算折扣
let discount = 0;
if (originalPrice && originalPrice > price) {
discount = Math.round((1 - price / originalPrice) * 100);
}
return `
<div class="product-card" data-product-id="${product.id}">
<div class="product-image">
<a href="product-detail.html?id=${product.id}">
<img src="${productImage}" alt="${productName}" loading="lazy">
</a>
${!isInStock ? '<span class="badge badge-sold-out">已售罄</span>' : ''}
${discount > 0 ? `<span class="badge badge-sale">-${discount}%</span>` : ''}
</div>
<div class="product-info">
<h3 class="product-title">
<a href="product-detail.html?id=${product.id}">${productName}</a>
</h3>
<div class="product-price">
<span class="price-current">¥${price.toFixed(2)}</span>
${originalPrice ? `<span class="price-original">¥${originalPrice.toFixed(2)}</span>` : ''}
</div>
<button class="btn btn-primary add-to-cart-btn" data-product-id="${product.id}" ${!isInStock ? 'disabled' : ''}>
${isInStock ? '<span>加入购物车</span>' : '<span>已售罄</span>'}
</button>
</div>
</div>
`;
}).join('');
$('#productGrid').html(productsHtml);
},
// 更新商品计数
updateProductCount() {
$('#productCount').text(this.totalCount);
},
// 渲染分页
renderPagination() {
const totalPages = Math.ceil(this.totalCount / this.itemsPerPage);
if (totalPages <= 1) {
$('.pagination').hide();
return;
}
$('.pagination').show();
// 更新上一页按钮状态
$('.page-btn.prev').prop('disabled', this.currentPage === 1);
// 更新下一页按钮状态
$('.page-btn.next').prop('disabled', this.currentPage === totalPages);
// 渲染页码
let pagesHtml = '';
const maxPages = 7;
let startPage = Math.max(1, this.currentPage - Math.floor(maxPages / 2));
let endPage = Math.min(totalPages, startPage + maxPages - 1);
if (endPage - startPage < maxPages - 1) {
startPage = Math.max(1, endPage - maxPages + 1);
}
if (startPage > 1) {
pagesHtml += `<button class="page-num" data-page="1">1</button>`;
if (startPage > 2) {
pagesHtml += `<span class="page-dots">...</span>`;
}
}
for (let i = startPage; i <= endPage; i++) {
pagesHtml += `<button class="page-num ${i === this.currentPage ? 'active' : ''}" data-page="${i}">${i}</button>`;
}
if (endPage < totalPages) {
if (endPage < totalPages - 1) {
pagesHtml += `<span class="page-dots">...</span>`;
}
pagesHtml += `<button class="page-num" data-page="${totalPages}">${totalPages}</button>`;
}
$('.page-numbers').html(pagesHtml);
},
// 绑定事件
bindEvents() {
// 筛选器事件
$('.filter-options input[type="checkbox"]').on('change', () => {
this.updateFilters();
});
$('#priceMin, #priceMax').on('input', utils.debounce(() => {
this.updateFilters();
}, 500));
$('.apply-filter').on('click', () => {
this.updateFilters();
});
$('.clear-filter').on('click', () => {
this.clearFilters();
});
// 排序事件
$('#sortSelect').on('change', (e) => {
this.sortBy = $(e.target).val();
this.currentPage = 1;
this.loadProducts();
});
// 分页事件
$('.page-btn.prev').on('click', () => {
if (this.currentPage > 1) {
this.currentPage--;
this.loadProducts();
window.scrollTo({ top: 0, behavior: 'smooth' });
}
});
$('.page-btn.next').on('click', () => {
const totalPages = Math.ceil(this.totalCount / this.itemsPerPage);
if (this.currentPage < totalPages) {
this.currentPage++;
this.loadProducts();
window.scrollTo({ top: 0, behavior: 'smooth' });
}
});
$(document).on('click', '.page-num', (e) => {
const page = parseInt($(e.target).data('page'));
if (page && page !== this.currentPage) {
this.currentPage = page;
this.loadProducts();
window.scrollTo({ top: 0, behavior: 'smooth' });
}
});
// 加入购物车事件
$(document).on('click', '.add-to-cart-btn', (e) => {
e.preventDefault();
e.stopPropagation();
const productId = $(e.currentTarget).data('product-id');
this.addToCart(productId);
});
// 监听语言切换
$(document).on('languageChanged', () => {
// 重新渲染分类(支持多语言)
this.renderCategories();
// 重新加载商品
this.loadProducts();
});
},
// 更新筛选条件
updateFilters() {
// 获取库存状态
this.filters.availability = [];
$('input[name="availability"]:checked').each((index, el) => {
this.filters.availability.push($(el).val());
});
// 获取价格区间
this.filters.priceMin = parseFloat($('#priceMin').val()) || null;
this.filters.priceMax = parseFloat($('#priceMax').val()) || null;
// 分类由点击事件处理,不需要在这里更新
// 重置到第一页
this.currentPage = 1;
// 重新加载商品
this.loadProducts();
},
// 清除筛选
clearFilters() {
$('input[type="checkbox"]').prop('checked', false);
$('#priceMin, #priceMax').val('');
// 清除分类选中状态
$('.category-tag').removeClass('active');
$('.category-parent').removeClass('active');
this.filters = {
availability: [],
priceMin: null,
priceMax: null,
category: []
};
this.currentPage = 1;
this.loadProducts();
},
// 加入购物车
addToCart(productId) {
console.log('加入购物车:', productId);
API.post('/cart', {
product_id: parseInt(productId),
quantity: 1
})
.then(() => {
Toast.success('已添加到购物车');
this.loadCartCount();
// 打开购物车抽屉(如果有)
if (typeof window.openCartDrawer === 'function') {
window.openCartDrawer();
}
})
.catch(error => {
console.error('添加到购物车失败:', error);
Toast.error(error.message || '添加失败');
});
},
// 加载购物车数量
loadCartCount() {
// 检查是否登录,未登录时不请求
const user = localStorage.getItem('currentUser');
if (!user) {
$('.cart-count').text(0);
return;
}
try {
const userData = JSON.parse(user);
if (!userData.token) {
$('.cart-count').text(0);
return;
}
} catch (e) {
$('.cart-count').text(0);
return;
}
API.get('/cart')
.then(data => {
const totalQuantity = data.total_quantity || 0;
$('.cart-count').text(totalQuantity);
})
.catch(error => {
console.error('加载购物车数量失败:', error);
$('.cart-count').text(0);
});
}
};
// 页面加载完成后初始化
$(document).ready(function() {
productList.init();
});