585 lines
22 KiB
JavaScript
585 lines
22 KiB
JavaScript
// 首页功能
|
||
|
||
// 加载轮播图
|
||
function loadBanners() {
|
||
console.log('=== 开始加载轮播图 ===');
|
||
|
||
// 调用后端轮播图 API
|
||
API.get('/banners')
|
||
.then(data => {
|
||
console.log('轮播图 API 返回数据:', data);
|
||
|
||
// 支持多种数据格式
|
||
let banners = [];
|
||
if (Array.isArray(data)) {
|
||
// 直接返回数组
|
||
banners = data;
|
||
} else if (data.data) {
|
||
// 数据在 data.data 中
|
||
banners = Array.isArray(data.data) ? data.data : (data.data.list || []);
|
||
} else if (data.list) {
|
||
// 数据在 data.list 中
|
||
banners = data.list;
|
||
} else if (data.banners) {
|
||
// 数据在 data.banners 中
|
||
banners = data.banners;
|
||
}
|
||
|
||
console.log('解析到的轮播图:', banners, '数量:', banners.length);
|
||
|
||
if (banners.length === 0) {
|
||
console.warn('没有可用的轮播图,使用默认轮播图');
|
||
// 没有轮播图时,使用 HTML 中的默认轮播图
|
||
heroSlider.init();
|
||
return;
|
||
}
|
||
|
||
renderBanners(banners);
|
||
// 渲染完成后初始化轮播图
|
||
heroSlider.init();
|
||
})
|
||
.catch(error => {
|
||
console.error('加载轮播图失败:', error);
|
||
// 失败时使用 HTML 中的默认轮播图,仍然初始化
|
||
heroSlider.init();
|
||
});
|
||
}
|
||
|
||
// 渲染轮播图
|
||
function renderBanners(banners) {
|
||
const bannersHtml = banners.map((banner, index) => {
|
||
// 处理链接
|
||
let link = '#';
|
||
let hasLink = false;
|
||
|
||
if (banner.link_type && banner.link_value) {
|
||
// link_type: 1=无链接, 2=商品详情, 3=分类页面, 4=外部链接
|
||
if (banner.link_type === 2) {
|
||
// 商品详情
|
||
link = `product-detail.html?id=${banner.link_value}`;
|
||
hasLink = true;
|
||
} else if (banner.link_type === 3) {
|
||
// 分类页面
|
||
link = `home.html?category=${banner.link_value}`;
|
||
hasLink = true;
|
||
} else if (banner.link_type === 4) {
|
||
// 外部链接
|
||
link = banner.link_value;
|
||
hasLink = true;
|
||
}
|
||
} else if (banner.link) {
|
||
// 兼容旧的 link 字段
|
||
link = banner.link;
|
||
hasLink = true;
|
||
}
|
||
|
||
const title = banner.title || `轮播图 ${index + 1}`;
|
||
const subtitle = banner.description || banner.subtitle || '';
|
||
const image = banner.image || `https://picsum.photos/1920/600?random=banner${index}`;
|
||
|
||
// 如果有链接,整个轮播图可点击
|
||
if (hasLink) {
|
||
return `
|
||
<div class="slide ${index === 0 ? 'active' : ''}">
|
||
<a href="${link}" class="slide-link" ${banner.link_type === 4 ? 'target="_blank" rel="noopener noreferrer"' : ''}>
|
||
<img src="${image}" alt="${title}" loading="${index === 0 ? 'eager' : 'lazy'}">
|
||
<div class="slide-content">
|
||
<h2>${title}</h2>
|
||
${subtitle ? `<p>${subtitle}</p>` : ''}
|
||
<span class="btn btn-primary" data-i18n="hero_btn">立即选购</span>
|
||
</div>
|
||
</a>
|
||
</div>
|
||
`;
|
||
} else {
|
||
// 无链接时,不可点击
|
||
return `
|
||
<div class="slide ${index === 0 ? 'active' : ''}">
|
||
<img src="${image}" alt="${title}" loading="${index === 0 ? 'eager' : 'lazy'}">
|
||
<div class="slide-content">
|
||
<h2>${title}</h2>
|
||
${subtitle ? `<p>${subtitle}</p>` : ''}
|
||
</div>
|
||
</div>
|
||
`;
|
||
}
|
||
}).join('');
|
||
|
||
$('#heroSlider').html(bannersHtml);
|
||
}
|
||
|
||
// 轮播图管理
|
||
const heroSlider = {
|
||
currentSlide: 0,
|
||
totalSlides: 0,
|
||
autoPlayInterval: null,
|
||
|
||
init() {
|
||
this.totalSlides = $('.slide').length;
|
||
this.createDots();
|
||
this.bindEvents();
|
||
this.startAutoPlay();
|
||
},
|
||
|
||
createDots() {
|
||
const dotsHtml = Array.from({ length: this.totalSlides }, (_, i) =>
|
||
`<span class="dot ${i === 0 ? 'active' : ''}" data-slide="${i}"></span>`
|
||
).join('');
|
||
$('.slider-dots').html(dotsHtml);
|
||
},
|
||
|
||
bindEvents() {
|
||
// 上一张
|
||
$('.slider-btn.prev').on('click', () => {
|
||
this.prevSlide();
|
||
});
|
||
|
||
// 下一张
|
||
$('.slider-btn.next').on('click', () => {
|
||
this.nextSlide();
|
||
});
|
||
|
||
// 点击圆点
|
||
$(document).on('click', '.dot', (e) => {
|
||
const slideIndex = parseInt($(e.target).data('slide'));
|
||
this.goToSlide(slideIndex);
|
||
});
|
||
|
||
// 鼠标悬停时暂停自动播放
|
||
$('.hero-slider').on('mouseenter', () => {
|
||
this.stopAutoPlay();
|
||
}).on('mouseleave', () => {
|
||
this.startAutoPlay();
|
||
});
|
||
},
|
||
|
||
goToSlide(index) {
|
||
$('.slide').removeClass('active');
|
||
$('.slide').eq(index).addClass('active');
|
||
|
||
$('.dot').removeClass('active');
|
||
$('.dot').eq(index).addClass('active');
|
||
|
||
this.currentSlide = index;
|
||
},
|
||
|
||
nextSlide() {
|
||
const nextIndex = (this.currentSlide + 1) % this.totalSlides;
|
||
this.goToSlide(nextIndex);
|
||
},
|
||
|
||
prevSlide() {
|
||
const prevIndex = (this.currentSlide - 1 + this.totalSlides) % this.totalSlides;
|
||
this.goToSlide(prevIndex);
|
||
},
|
||
|
||
startAutoPlay() {
|
||
this.stopAutoPlay();
|
||
this.autoPlayInterval = setInterval(() => {
|
||
this.nextSlide();
|
||
}, 5000);
|
||
},
|
||
|
||
stopAutoPlay() {
|
||
if (this.autoPlayInterval) {
|
||
clearInterval(this.autoPlayInterval);
|
||
}
|
||
}
|
||
};
|
||
|
||
// 畅销商品数据(与product-list.js共用)
|
||
const DEFAULT_IMAGE = 'https://picsum.photos/400/400?random=';
|
||
|
||
const bestsellersData = [
|
||
{
|
||
id: 1,
|
||
name: 'Christmas DIY Poke Fun',
|
||
name_en: 'Christmas DIY Poke Fun',
|
||
name_ja: 'クリスマスDIYポークファン',
|
||
price: 21.99,
|
||
originalPrice: 49.99,
|
||
image: DEFAULT_IMAGE + 'bs1',
|
||
rating: 0,
|
||
reviews: 0,
|
||
badges: ['sale'],
|
||
discount: 57
|
||
},
|
||
{
|
||
id: 2,
|
||
name: 'Creative Costume Collage Set: Sweetheart',
|
||
name_en: 'Creative Costume Collage Set: Sweetheart',
|
||
name_ja: 'クリエイティブコスチュームコラージュセット: スイートハート',
|
||
price: 21.99,
|
||
originalPrice: 29.99,
|
||
image: DEFAULT_IMAGE + 'bs2',
|
||
rating: 4.87,
|
||
reviews: 23,
|
||
badges: ['hot'],
|
||
discount: 27
|
||
},
|
||
{
|
||
id: 3,
|
||
name: '3-in-1 Dress Up Game Set: Princess Fantasy Makeup',
|
||
name_en: '3-in-1 Dress Up Game Set: Princess Fantasy Makeup',
|
||
name_ja: '3-in-1 ドレスアップゲームセット: プリンセスファンタジーメイク',
|
||
price: 19.99,
|
||
originalPrice: 34.99,
|
||
image: DEFAULT_IMAGE + 'bs3',
|
||
rating: 4.86,
|
||
reviews: 163,
|
||
badges: ['hot'],
|
||
discount: 43
|
||
},
|
||
{
|
||
id: 4,
|
||
name: 'Magic Christmas Tree',
|
||
name_en: 'Magic Christmas Tree',
|
||
name_ja: 'マジッククリスマスツリー',
|
||
price: 11.99,
|
||
originalPrice: 19.99,
|
||
image: DEFAULT_IMAGE + 'bs4',
|
||
rating: 4.8,
|
||
reviews: 15,
|
||
badges: ['sale'],
|
||
discount: 41
|
||
}
|
||
];
|
||
|
||
// 渲染畅销商品(使用真实 API 请求热门商品)
|
||
function renderBestsellers() {
|
||
// 显示加载中状态
|
||
$('#bestsellersGrid').html('<div class="loading-spinner">加载中...</div>');
|
||
|
||
// 调用后端热门商品 API
|
||
API.get('/frontend/products/hot', { page: 1, page_size: 8 })
|
||
.then(data => {
|
||
console.log('热门商品 API 返回数据:', data);
|
||
|
||
// 支持多种数据格式
|
||
const products = data.data?.list || data.list || [];
|
||
|
||
if (products.length === 0) {
|
||
$('#bestsellersGrid').html('<div class="empty-state">暂无热门商品</div>');
|
||
return;
|
||
}
|
||
|
||
console.log('解析到的商品:', products);
|
||
renderProductCards(products);
|
||
})
|
||
.catch(error => {
|
||
console.error('加载热门商品失败:', error);
|
||
$('#bestsellersGrid').html('<div class="error-state">加载失败,请刷新页面</div>');
|
||
Toast.error(error.message || '加载热门商品失败');
|
||
});
|
||
}
|
||
|
||
// 渲染商品卡片
|
||
function renderProductCards(products) {
|
||
const productsHtml = products.map(product => {
|
||
// 处理价格:分 → 元
|
||
const minPrice = parseInt(product.minSalePrice || product.price || 0);
|
||
const maxPrice = parseInt(product.maxSalePrice || product.price || 0);
|
||
const minLinePrice = parseInt(product.minLinePrice || product.original_price || 0);
|
||
const maxLinePrice = parseInt(product.maxLinePrice || product.original_price || 0);
|
||
|
||
const currentPrice = PriceUtils.fenToYuan(minPrice);
|
||
const originalPrice = minLinePrice > 0 ? PriceUtils.fenToYuan(minLinePrice) : null;
|
||
|
||
// 计算折扣
|
||
let discount = 0;
|
||
if (originalPrice && originalPrice > currentPrice) {
|
||
discount = Math.round(((originalPrice - currentPrice) / originalPrice) * 100);
|
||
}
|
||
|
||
// 商品图片
|
||
const productImage = product.primaryImage || product.main_image || product.image || 'https://picsum.photos/400/400?random=' + (product.spuId || product.id);
|
||
|
||
// 商品名称
|
||
const productName = product.title || product.name || '未知商品';
|
||
|
||
// 商品ID
|
||
const productId = product.spuId || product.id;
|
||
|
||
// 库存
|
||
const stock = product.spuStockQuantity || product.stock || 0;
|
||
|
||
// 徽章
|
||
let badges = '';
|
||
if (stock <= 0) {
|
||
badges += '<span class="badge badge-danger">售罄</span>';
|
||
} else if (stock < 10) {
|
||
badges += '<span class="badge badge-warning">仅剩' + stock + '件</span>';
|
||
}
|
||
if (discount > 30) {
|
||
badges += '<span class="badge badge-success">特惠</span>';
|
||
}
|
||
// 根据销量添加热门徽章
|
||
const soldNum = product.soldNum || 0;
|
||
if (soldNum > 100) {
|
||
badges += '<span class="badge badge-warning">热门</span>';
|
||
}
|
||
|
||
// 评分和评价数(当前 API 没有返回,暂不显示)
|
||
const rating = product.rating || 0;
|
||
const reviewCount = product.comment_count || 0;
|
||
const ratingHtml = reviewCount > 0 ? `
|
||
<div class="product-rating">
|
||
<span class="stars">${'★'.repeat(Math.round(rating))}${'☆'.repeat(5 - Math.round(rating))}</span>
|
||
<span class="review-count">${reviewCount} ${i18n.t('reviews')}</span>
|
||
</div>
|
||
` : '';
|
||
|
||
return `
|
||
<div class="product-card" data-product-id="${productId}">
|
||
<div class="product-image">
|
||
<img src="${productImage}" alt="${productName}" loading="lazy">
|
||
<div class="product-badges">
|
||
${badges}
|
||
</div>
|
||
</div>
|
||
<div class="product-info">
|
||
<h3 class="product-title">${productName}</h3>
|
||
${ratingHtml}
|
||
<div class="product-price">
|
||
<span class="price-current">¥${currentPrice.toFixed(2)}</span>
|
||
${originalPrice ? `<span class="price-original">¥${originalPrice.toFixed(2)}</span>` : ''}
|
||
${discount > 0 ? `<span class="price-discount">${i18n.t('save')} ${discount}%</span>` : ''}
|
||
</div>
|
||
<div class="product-actions">
|
||
<button class="btn btn-primary add-to-cart" data-product-id="${productId}" ${stock <= 0 ? 'disabled' : ''}>
|
||
${stock <= 0 ? '已售罄' : i18n.t('add_to_cart')}
|
||
</button>
|
||
<button class="btn btn-quick-view quick-view" data-product-id="${productId}">
|
||
${i18n.t('quick_view')}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
}).join('');
|
||
|
||
$('#bestsellersGrid').html(productsHtml);
|
||
|
||
// 绑定事件
|
||
bindProductEvents();
|
||
}
|
||
|
||
// 渲染用户评价
|
||
function renderReviews() {
|
||
// 加载高分评价
|
||
loadHighRatingReviews();
|
||
}
|
||
|
||
// 加载高分评价(直接调用专门的评论API)
|
||
function loadHighRatingReviews() {
|
||
console.log('=== 开始加载高分评价 ===');
|
||
|
||
// 直接调用高分评论API,一次请求获取所有数据
|
||
API.get('/comments/high-rating', { limit: 6 })
|
||
.then(data => {
|
||
const comments = data.data || data || [];
|
||
console.log('获取到高分评论:', comments.length, '条');
|
||
|
||
if (comments.length === 0) {
|
||
renderEmptyReviews();
|
||
return;
|
||
}
|
||
|
||
// 为每个评论添加商品名称
|
||
const reviews = comments.map(comment => ({
|
||
...comment,
|
||
productName: comment.product?.name || comment.product?.title || '商品'
|
||
}));
|
||
|
||
renderReviewCards(reviews);
|
||
})
|
||
.catch(error => {
|
||
console.error('加载高分评价失败:', error);
|
||
renderEmptyReviews(true);
|
||
});
|
||
}
|
||
|
||
// 渲染评论卡片
|
||
function renderReviewCards(reviews) {
|
||
const reviewsHtml = reviews.map(review => {
|
||
// 字段映射
|
||
const rating = review.rating || 5;
|
||
const content = review.content || '';
|
||
const userName = review.user?.nickname || review.user?.name || '匿名用户';
|
||
const productName = review.productName || '商品';
|
||
const createdAt = review.created_at || review.createdAt || '';
|
||
const images = review.images || [];
|
||
|
||
// 格式化日期
|
||
let dateStr = '';
|
||
if (createdAt) {
|
||
const date = new Date(createdAt);
|
||
dateStr = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
|
||
}
|
||
|
||
// 生成星星
|
||
const stars = '★'.repeat(rating) + '☆'.repeat(5 - rating);
|
||
|
||
return `
|
||
<div class="review-card">
|
||
<div class="review-rating">${stars}</div>
|
||
<p class="review-text">${content}</p>
|
||
${images.length > 0 ? `
|
||
<div class="review-images">
|
||
${images.slice(0, 3).map(img => `
|
||
<img src="${img}" alt="评论图片" loading="lazy">
|
||
`).join('')}
|
||
</div>
|
||
` : ''}
|
||
<p class="review-product">商品:${productName}</p>
|
||
<div class="review-author">
|
||
<span class="review-author-name">${userName}</span>
|
||
${dateStr ? `<span class="review-date">${dateStr}</span>` : ''}
|
||
</div>
|
||
</div>
|
||
`;
|
||
}).join('');
|
||
|
||
$('#reviewsGrid').html(reviewsHtml);
|
||
}
|
||
|
||
// 渲染评论空状态
|
||
function renderEmptyReviews(isError = false) {
|
||
const lang = i18n.currentLang;
|
||
const emptyTitle = isError ?
|
||
(lang === 'en-US' ? 'Failed to load reviews' : lang === 'ja-JP' ? 'レビューの読み込みに失敗しました' : '加载评论失败') :
|
||
(lang === 'en-US' ? 'No reviews yet' : lang === 'ja-JP' ? 'まだレビューはありません' : '暂无评价');
|
||
const emptyDesc = isError ?
|
||
(lang === 'en-US' ? 'Please try again later' : lang === 'ja-JP' ? '後でもう一度お試しください' : '请稍后再试') :
|
||
(lang === 'en-US' ? 'Be the first to share your thoughts about our products!' : lang === 'ja-JP' ? '最初に製品についてのご意見をお聞かせください!' : '成为第一个分享购物体验的用户吧!');
|
||
const btnText = lang === 'en-US' ? 'Shop Now' : lang === 'ja-JP' ? '今すぐショッピング' : '开始购物';
|
||
|
||
$('#reviewsGrid').html(`
|
||
<div class="empty-reviews-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"/>
|
||
${isError ? `
|
||
<path d="M45 45 L75 75 M75 45 L45 75" stroke="#FF6B6B" stroke-width="4" stroke-linecap="round"/>
|
||
` : `
|
||
<path d="M60 35 L65 50 L80 52 L70 62 L73 77 L60 70 L47 77 L50 62 L40 52 L55 50 Z" fill="#FFD93D" stroke="#FFB800" stroke-width="2"/>
|
||
<circle cx="35" cy="45" r="3" fill="#FFD93D"/>
|
||
<circle cx="85" cy="45" r="3" fill="#FFD93D"/>
|
||
<circle cx="40" cy="75" r="2" fill="#FFD93D"/>
|
||
<circle cx="80" cy="75" r="2" fill="#FFD93D"/>
|
||
`}
|
||
</svg>
|
||
</div>
|
||
<h3 class="empty-title">${emptyTitle}</h3>
|
||
<p class="empty-desc">${emptyDesc}</p>
|
||
${!isError ? `<a href="home.html" class="btn btn-primary empty-btn">${btnText}</a>` : ''}
|
||
</div>
|
||
`);
|
||
}
|
||
|
||
// 绑定商品事件
|
||
function bindProductEvents() {
|
||
// 加入购物车
|
||
$('.add-to-cart').off('click').on('click', function(e) {
|
||
e.stopPropagation();
|
||
const productId = parseInt($(this).data('product-id'));
|
||
|
||
console.log('添加商品到购物车:', productId);
|
||
|
||
// 先获取商品详情,确定默认SKU
|
||
API.get(`/products/${productId}`)
|
||
.then(productData => {
|
||
const product = productData.data || productData;
|
||
console.log('商品详情:', product);
|
||
|
||
// 构建购物车请求数据
|
||
const cartData = {
|
||
product_id: productId,
|
||
quantity: 1
|
||
};
|
||
|
||
// 如果商品有 SKU,选择第一个可用的 SKU
|
||
if (product.skus && product.skus.length > 0) {
|
||
// 查找第一个有库存的 SKU
|
||
const availableSku = product.skus.find(sku => (sku.stock || 0) > 0);
|
||
if (availableSku) {
|
||
cartData.sku_id = parseInt(availableSku.id || availableSku.sku_id);
|
||
console.log('选择 SKU:', cartData.sku_id);
|
||
} else {
|
||
// 如果没有库存,使用第一个 SKU
|
||
cartData.sku_id = parseInt(product.skus[0].id || product.skus[0].sku_id);
|
||
console.log('无库存,使用第一个 SKU:', cartData.sku_id);
|
||
}
|
||
}
|
||
|
||
console.log('添加到购物车的数据:', cartData);
|
||
|
||
// 使用 API 添加到购物车
|
||
return API.post('/cart', cartData);
|
||
})
|
||
.then(() => {
|
||
console.log('=== 添加到购物车成功 ===');
|
||
|
||
// 显示成功提示
|
||
let message = '已添加到购物车';
|
||
if (typeof i18n !== 'undefined' && i18n.t) {
|
||
const translated = i18n.t('product_added_to_cart');
|
||
if (translated && translated !== 'product_added_to_cart') {
|
||
message = translated;
|
||
}
|
||
}
|
||
console.log('显示 Toast 提示:', message);
|
||
Toast.success(message);
|
||
|
||
// 更新购物车计数
|
||
console.log('更新购物车计数...');
|
||
console.log('window.cart 存在:', !!window.cart);
|
||
console.log('cart 存在:', typeof cart !== 'undefined');
|
||
|
||
if (window.cart && typeof window.cart.loadCart === 'function') {
|
||
console.log('调用 window.cart.loadCart()');
|
||
window.cart.loadCart();
|
||
} else if (typeof cart !== 'undefined' && typeof cart.loadCart === 'function') {
|
||
console.log('调用 cart.loadCart()');
|
||
cart.loadCart();
|
||
} else {
|
||
console.error('cart.loadCart 方法不存在');
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('添加到购物车失败:', error);
|
||
Toast.error(error.message || '添加失败');
|
||
});
|
||
});
|
||
|
||
// 快速查看
|
||
$('.quick-view').off('click').on('click', function(e) {
|
||
e.stopPropagation();
|
||
const productId = $(this).data('product-id');
|
||
window.location.href = `product-detail.html?id=${productId}`;
|
||
});
|
||
|
||
// 点击卡片跳转
|
||
$('.product-card').off('click').on('click', function() {
|
||
const productId = $(this).data('product-id');
|
||
window.location.href = `product-detail.html?id=${productId}`;
|
||
});
|
||
}
|
||
|
||
// 页面初始化
|
||
$(document).ready(function() {
|
||
// 加载轮播图(使用真实 API)
|
||
loadBanners();
|
||
|
||
// 渲染畅销商品
|
||
renderBestsellers();
|
||
|
||
// 渲染用户评价
|
||
renderReviews();
|
||
|
||
// 监听语言切换
|
||
$(document).on('languageChanged', function() {
|
||
renderBestsellers();
|
||
});
|
||
});
|