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

585 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.

// 首页功能
// 加载轮播图
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();
});
});