web
This commit is contained in:
425
web/assets/js/search.js
Normal file
425
web/assets/js/search.js
Normal file
@@ -0,0 +1,425 @@
|
||||
// 搜索页面逻辑
|
||||
const searchPage = {
|
||||
// 搜索参数
|
||||
keyword: '',
|
||||
filters: {
|
||||
priceMin: null,
|
||||
priceMax: null
|
||||
},
|
||||
sortBy: 'default',
|
||||
sortType: 'desc',
|
||||
currentPage: 1,
|
||||
itemsPerPage: 20,
|
||||
totalCount: 0,
|
||||
|
||||
// 初始化
|
||||
init() {
|
||||
console.log('=== 搜索页面初始化 ===');
|
||||
|
||||
// 从URL获取搜索关键词
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
this.keyword = urlParams.get('q') || '';
|
||||
|
||||
console.log('搜索关键词:', this.keyword);
|
||||
|
||||
if (!this.keyword) {
|
||||
// 如果没有搜索关键词,跳转回首页
|
||||
console.warn('没有搜索关键词,跳转到首页');
|
||||
window.location.href = 'index.html';
|
||||
return;
|
||||
}
|
||||
|
||||
// 显示搜索关键词
|
||||
$('#searchKeyword').text(this.keyword);
|
||||
$('#searchInput').val(this.keyword);
|
||||
|
||||
// 绑定事件
|
||||
this.bindEvents();
|
||||
|
||||
// 执行搜索
|
||||
this.search();
|
||||
|
||||
// 加载购物车数量
|
||||
this.loadCartCount();
|
||||
},
|
||||
|
||||
// 绑定事件
|
||||
bindEvents() {
|
||||
// 排序选择
|
||||
$('#sortSelect').on('change', (e) => {
|
||||
const value = $(e.target).val();
|
||||
this.handleSort(value);
|
||||
});
|
||||
|
||||
// 应用筛选
|
||||
$('.apply-filter').on('click', () => {
|
||||
this.applyFilters();
|
||||
});
|
||||
|
||||
// 清除筛选
|
||||
$('.clear-filter').on('click', () => {
|
||||
this.clearFilters();
|
||||
});
|
||||
|
||||
// 价格输入框回车
|
||||
$('#priceMin, #priceMax').on('keypress', (e) => {
|
||||
if (e.which === 13) {
|
||||
this.applyFilters();
|
||||
}
|
||||
});
|
||||
|
||||
// 分页按钮
|
||||
$(document).on('click', '.page-btn.prev', () => {
|
||||
if (this.currentPage > 1) {
|
||||
this.currentPage--;
|
||||
this.search();
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on('click', '.page-btn.next', () => {
|
||||
const totalPages = Math.ceil(this.totalCount / this.itemsPerPage);
|
||||
if (this.currentPage < totalPages) {
|
||||
this.currentPage++;
|
||||
this.search();
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on('click', '.page-num', (e) => {
|
||||
const page = parseInt($(e.target).data('page'));
|
||||
if (page && page !== this.currentPage) {
|
||||
this.currentPage = page;
|
||||
this.search();
|
||||
}
|
||||
});
|
||||
|
||||
// 搜索按钮
|
||||
$('.search-btn').on('click', () => {
|
||||
this.performNewSearch();
|
||||
});
|
||||
|
||||
// 搜索框回车
|
||||
$('#searchInput').on('keypress', (e) => {
|
||||
if (e.which === 13) {
|
||||
this.performNewSearch();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 执行新搜索
|
||||
performNewSearch() {
|
||||
const newKeyword = $('#searchInput').val().trim();
|
||||
if (newKeyword && newKeyword !== this.keyword) {
|
||||
window.location.href = `search.html?q=${encodeURIComponent(newKeyword)}`;
|
||||
}
|
||||
},
|
||||
|
||||
// 处理排序
|
||||
handleSort(sortValue) {
|
||||
console.log('排序选择:', sortValue);
|
||||
|
||||
switch (sortValue) {
|
||||
case 'price_asc':
|
||||
this.sortBy = 'price';
|
||||
this.sortType = 'asc';
|
||||
break;
|
||||
case 'price_desc':
|
||||
this.sortBy = 'price';
|
||||
this.sortType = 'desc';
|
||||
break;
|
||||
case 'sales_desc':
|
||||
this.sortBy = 'sales';
|
||||
this.sortType = 'desc';
|
||||
break;
|
||||
default:
|
||||
this.sortBy = 'default';
|
||||
this.sortType = 'desc';
|
||||
}
|
||||
|
||||
this.currentPage = 1;
|
||||
this.search();
|
||||
},
|
||||
|
||||
// 应用筛选
|
||||
applyFilters() {
|
||||
const minPrice = parseFloat($('#priceMin').val()) || null;
|
||||
const maxPrice = parseFloat($('#priceMax').val()) || null;
|
||||
|
||||
// 验证价格范围
|
||||
if (minPrice !== null && maxPrice !== null && minPrice > maxPrice) {
|
||||
Toast.show({
|
||||
type: 'error',
|
||||
message: i18n.t('error_price_range')
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.filters.priceMin = minPrice;
|
||||
this.filters.priceMax = maxPrice;
|
||||
this.currentPage = 1;
|
||||
|
||||
console.log('应用筛选:', this.filters);
|
||||
this.search();
|
||||
},
|
||||
|
||||
// 清除筛选
|
||||
clearFilters() {
|
||||
this.filters.priceMin = null;
|
||||
this.filters.priceMax = null;
|
||||
$('#priceMin').val('');
|
||||
$('#priceMax').val('');
|
||||
this.currentPage = 1;
|
||||
|
||||
console.log('清除筛选');
|
||||
this.search();
|
||||
},
|
||||
|
||||
// 执行搜索
|
||||
search() {
|
||||
console.log('执行搜索:', {
|
||||
keyword: this.keyword,
|
||||
page: this.currentPage,
|
||||
filters: this.filters,
|
||||
sort: this.sortBy,
|
||||
sortType: this.sortType
|
||||
});
|
||||
|
||||
// 显示加载状态
|
||||
$('#productGrid').html('<div class="loading">加载中...</div>');
|
||||
|
||||
// 构建API参数
|
||||
const params = {
|
||||
keyword: this.keyword,
|
||||
page: this.currentPage,
|
||||
page_size: this.itemsPerPage
|
||||
};
|
||||
|
||||
// 添加价格筛选参数(元转分)
|
||||
if (this.filters.priceMin !== null) {
|
||||
params.min_price = Math.round(this.filters.priceMin * 100);
|
||||
}
|
||||
if (this.filters.priceMax !== null) {
|
||||
params.max_price = Math.round(this.filters.priceMax * 100);
|
||||
}
|
||||
|
||||
// 添加排序参数
|
||||
if (this.sortBy !== 'default') {
|
||||
params.sort_by = this.sortBy;
|
||||
params.sort_order = this.sortType;
|
||||
}
|
||||
|
||||
// 调用搜索API
|
||||
API.get('/products/search', params)
|
||||
.then(data => {
|
||||
console.log('搜索结果:', data);
|
||||
|
||||
if (data && data.list) {
|
||||
this.totalCount = data.total || 0;
|
||||
this.renderProducts(data.list);
|
||||
this.updateProductCount();
|
||||
this.renderPagination();
|
||||
} else {
|
||||
this.totalCount = 0;
|
||||
this.renderEmptyState();
|
||||
}
|
||||
|
||||
// 滚动到顶部
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('搜索失败:', error);
|
||||
Toast.show({
|
||||
type: 'error',
|
||||
message: error.message || i18n.t('error_load_products')
|
||||
});
|
||||
this.renderEmptyState();
|
||||
});
|
||||
},
|
||||
|
||||
// 渲染商品列表 (与product-list.js保持一致)
|
||||
renderProducts(products) {
|
||||
const lang = i18n.currentLang;
|
||||
|
||||
if (!products || products.length === 0) {
|
||||
this.renderEmptyState();
|
||||
return;
|
||||
}
|
||||
|
||||
const productsHtml = products.map(product => {
|
||||
const productName = this.getProductName(product, lang) || '未知商品';
|
||||
const productImage = product.main_image || 'https://picsum.photos/400/400?random=default';
|
||||
|
||||
// 价格处理 - 后端API返回的是分,需要转换为元
|
||||
const price = (product.price || 0) / 100;
|
||||
const originalPrice = product.orig_price ? (product.orig_price / 100) : 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">' + i18n.t('out_of_stock') + '</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>' + i18n.t('add_to_cart') + '</span>' : '<span>' + i18n.t('out_of_stock') + '</span>'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
|
||||
$('#productGrid').html(productsHtml);
|
||||
|
||||
// 绑定加入购物车事件
|
||||
this.bindProductEvents();
|
||||
},
|
||||
|
||||
// 渲染空状态 (与product-list.js保持一致)
|
||||
renderEmptyState() {
|
||||
$('#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">${i18n.t('search_no_results')}</h3>
|
||||
<p class="empty-desc">${i18n.t('search_try_again')}</p>
|
||||
<a href="home.html" class="btn btn-primary empty-btn">${i18n.t('browse_all_products')}</a>
|
||||
</div>
|
||||
`);
|
||||
|
||||
$('.pagination').hide();
|
||||
},
|
||||
|
||||
// 更新商品计数
|
||||
updateProductCount() {
|
||||
$('#productCount').text(this.totalCount);
|
||||
},
|
||||
|
||||
// 渲染分页 (与product-list.js保持一致)
|
||||
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);
|
||||
},
|
||||
|
||||
// 绑定商品事件
|
||||
bindProductEvents() {
|
||||
$('.add-to-cart-btn').off('click').on('click', (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const productId = $(e.currentTarget).data('product-id');
|
||||
this.addToCart(productId);
|
||||
});
|
||||
},
|
||||
|
||||
// 加入购物车
|
||||
addToCart(productId) {
|
||||
console.log('加入购物车:', productId);
|
||||
|
||||
// 检查是否登录
|
||||
if (!cart.isLoggedIn()) {
|
||||
Toast.warning(i18n.t('please_login_first'));
|
||||
setTimeout(() => {
|
||||
window.location.href = 'login.html?redirect=' + encodeURIComponent(window.location.href);
|
||||
}, 1500);
|
||||
return;
|
||||
}
|
||||
|
||||
// 调用购物车API
|
||||
CartAPI.addToCart(productId, 0, 1)
|
||||
.then(() => {
|
||||
Toast.success(i18n.t('add_to_cart_success'));
|
||||
|
||||
// 更新购物车数量
|
||||
cart.updateCartCount();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('加入购物车失败:', error);
|
||||
Toast.error(error.message || i18n.t('add_to_cart_failed'));
|
||||
});
|
||||
},
|
||||
|
||||
// 加载购物车数量
|
||||
loadCartCount() {
|
||||
if (cart && typeof cart.updateCartCount === 'function') {
|
||||
cart.updateCartCount();
|
||||
}
|
||||
},
|
||||
|
||||
// 获取商品名称(多语言)
|
||||
getProductName(product, lang) {
|
||||
if (lang === 'en-US' && product.name_en) {
|
||||
return product.name_en;
|
||||
}
|
||||
if (lang === 'ja-JP' && product.name_ja) {
|
||||
return product.name_ja;
|
||||
}
|
||||
return product.name || '';
|
||||
}
|
||||
};
|
||||
|
||||
// 页面加载完成后初始化
|
||||
$(document).ready(function() {
|
||||
searchPage.init();
|
||||
});
|
||||
Reference in New Issue
Block a user