Files
ai_dianshang/web/assets/js/product-list.js
2025-11-28 15:18:10 +08:00

615 lines
22 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 商品列表页面逻辑 - 使用真实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();
});