web
This commit is contained in:
757
web/assets/js/product-detail.js
Normal file
757
web/assets/js/product-detail.js
Normal file
@@ -0,0 +1,757 @@
|
||||
// 商品详情页功能 - 使用真实API
|
||||
|
||||
let currentProduct = null;
|
||||
let selectedSku = null;
|
||||
let currentCommentPage = 1;
|
||||
let currentCommentFilter = 'all'; // all, good, medium, bad, image
|
||||
|
||||
// 页面初始化
|
||||
$(document).ready(function() {
|
||||
const productId = getProductIdFromUrl();
|
||||
if (productId) {
|
||||
loadProductDetail(productId);
|
||||
loadCommentsCount(productId);
|
||||
loadComments(productId);
|
||||
} else {
|
||||
Toast.error('商品不存在');
|
||||
setTimeout(() => {
|
||||
window.location.href = 'index.html';
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
bindEvents();
|
||||
loadCartCount();
|
||||
});
|
||||
|
||||
// 从URL获取商品ID
|
||||
function getProductIdFromUrl() {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
return urlParams.get('id');
|
||||
}
|
||||
|
||||
// 加载商品详情
|
||||
function loadProductDetail(productId) {
|
||||
console.log('加载商品详情:', productId);
|
||||
|
||||
API.get(`/frontend/products/${productId}/detail`)
|
||||
.then(data => {
|
||||
console.log('商品详情数据:', data);
|
||||
currentProduct = data;
|
||||
renderProductDetail(data);
|
||||
loadRelatedProducts();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('加载商品详情失败:', error);
|
||||
Toast.error(error.message || '加载商品详情失败');
|
||||
setTimeout(() => {
|
||||
window.location.href = 'index.html';
|
||||
}, 2000);
|
||||
});
|
||||
}
|
||||
|
||||
// 渲染商品详情
|
||||
function renderProductDetail(product) {
|
||||
const lang = i18n.currentLang;
|
||||
|
||||
// 设置商品名称
|
||||
const productName = product.title || product.name || '未知商品';
|
||||
$('#productName').text(productName);
|
||||
$('#breadcrumbProduct').text(productName);
|
||||
document.title = `${productName} - vizee为之甄选`;
|
||||
|
||||
// 设置价格
|
||||
const minPrice = PriceUtils.fenToYuan(product.minSalePrice || product.price || 0);
|
||||
const maxPrice = PriceUtils.fenToYuan(product.maxSalePrice || product.price || 0);
|
||||
const originPrice = PriceUtils.fenToYuan(product.maxLinePrice || product.originPrice || 0);
|
||||
|
||||
if (minPrice === maxPrice) {
|
||||
$('#currentPrice').text(`¥${minPrice.toFixed(2)}`);
|
||||
} else {
|
||||
$('#currentPrice').text(`¥${minPrice.toFixed(2)} - ¥${maxPrice.toFixed(2)}`);
|
||||
}
|
||||
|
||||
if (originPrice > maxPrice) {
|
||||
$('#originalPrice').text(`¥${originPrice.toFixed(2)}`).show();
|
||||
const discount = Math.round((1 - maxPrice / originPrice) * 100);
|
||||
$('#priceSave').text(`${i18n.t('save') || '省'} ${discount}%`).show();
|
||||
} else {
|
||||
$('#originalPrice').hide();
|
||||
$('#priceSave').hide();
|
||||
}
|
||||
|
||||
// 设置库存状态
|
||||
const spuStock = product.spuStockQuantity ?? product.stock ?? 0;
|
||||
const isInStock = spuStock > 0;
|
||||
|
||||
if (isInStock) {
|
||||
$('#stockStatus').text('有货').addClass('in-stock').removeClass('out-of-stock');
|
||||
} else {
|
||||
$('#stockStatus').text('缺货').addClass('out-of-stock').removeClass('in-stock');
|
||||
}
|
||||
|
||||
// 保存库存状态到currentProduct
|
||||
currentProduct.isStock = isInStock;
|
||||
|
||||
// 设置图片
|
||||
loadProductImages(product.images || [product.primaryImage]);
|
||||
|
||||
// 设置描述
|
||||
const description = product.descriptionText || product.details || '';
|
||||
$('#productDescription').text(description);
|
||||
|
||||
// 渲染详情图片
|
||||
if (product.desc && Array.isArray(product.desc) && product.desc.length > 0) {
|
||||
const detailImagesHtml = product.desc.map(img =>
|
||||
`<img src="${img}" alt="商品详情" loading="lazy">`
|
||||
).join('');
|
||||
$('#productDetailImages').html(detailImagesHtml);
|
||||
}
|
||||
|
||||
// 渲染规格选择器
|
||||
if (product.specList && product.specList.length > 0) {
|
||||
renderSpecSelector(product.specList, product.skuList);
|
||||
} else {
|
||||
$('#specSelector').hide();
|
||||
}
|
||||
|
||||
// 显示已售数量
|
||||
if (product.soldNum) {
|
||||
$('#soldCount').text(`已售 ${product.soldNum}`).show();
|
||||
}
|
||||
}
|
||||
|
||||
// 加载商品图片
|
||||
function loadProductImages(images) {
|
||||
// 显示加载动画
|
||||
showImageLoading();
|
||||
|
||||
if (!images || images.length === 0) {
|
||||
// 如果没有图片,隐藏加载动画并显示默认图片
|
||||
hideImageLoading();
|
||||
images = ['https://picsum.photos/800/800?random=default'];
|
||||
}
|
||||
|
||||
// 主图
|
||||
const mainImg = new Image();
|
||||
mainImg.onload = function() {
|
||||
$('#mainImage').attr('src', images[0]);
|
||||
hideImageLoading();
|
||||
};
|
||||
mainImg.onerror = function() {
|
||||
// 加载失败,使用占位图
|
||||
$('#mainImage').attr('src', 'https://picsum.photos/800/800?random=error');
|
||||
hideImageLoading();
|
||||
};
|
||||
mainImg.src = images[0];
|
||||
|
||||
// 缩略图
|
||||
const thumbnailsHtml = images.map((img, index) =>
|
||||
`<div class="thumbnail ${index === 0 ? 'active' : ''}" data-index="${index}">
|
||||
<img src="${img}" alt="商品图片 ${index + 1}" loading="lazy">
|
||||
</div>`
|
||||
).join('');
|
||||
|
||||
$('#thumbnails').html(thumbnailsHtml);
|
||||
}
|
||||
|
||||
// 显示图片加载动画
|
||||
function showImageLoading() {
|
||||
const loadingHtml = `
|
||||
<div class="image-loading" id="imageLoading">
|
||||
<div class="loading-spinner"></div>
|
||||
<div class="loading-text">加载中...</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
if ($('#imageLoading').length === 0) {
|
||||
$('.main-image').append(loadingHtml);
|
||||
}
|
||||
$('#mainImage').css('opacity', '0');
|
||||
}
|
||||
|
||||
// 隐藏图片加载动画
|
||||
function hideImageLoading() {
|
||||
$('#imageLoading').fadeOut(300, function() {
|
||||
$(this).remove();
|
||||
});
|
||||
$('#mainImage').css('opacity', '1');
|
||||
}
|
||||
|
||||
// 渲染规格选择器
|
||||
function renderSpecSelector(specList, skuList) {
|
||||
let specHtml = '';
|
||||
|
||||
specList.forEach((spec, specIndex) => {
|
||||
// 优先使用title,其次specName,最后name
|
||||
const specName = spec.title || spec.specName || spec.name || `规格${specIndex + 1}`;
|
||||
const specValues = spec.specValueList || [];
|
||||
|
||||
specHtml += `
|
||||
<div class="spec-group">
|
||||
<label class="spec-label">${specName}</label>
|
||||
<div class="spec-options" data-spec-index="${specIndex}">
|
||||
${specValues.map((value, valueIndex) => {
|
||||
const valueName = value.specValue || value.name || value;
|
||||
return `
|
||||
<button class="spec-option"
|
||||
data-spec-index="${specIndex}"
|
||||
data-value-index="${valueIndex}"
|
||||
data-spec-value="${valueName}">
|
||||
${valueName}
|
||||
</button>
|
||||
`;
|
||||
}).join('')}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
|
||||
$('#specSelector').html(specHtml).show();
|
||||
}
|
||||
|
||||
// 加载相关商品
|
||||
function loadRelatedProducts() {
|
||||
API.get('/products', { page: 1, page_size: 4 })
|
||||
.then(data => {
|
||||
const products = data.list || [];
|
||||
renderRelatedProducts(products.slice(0, 4));
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('加载相关商品失败:', error);
|
||||
});
|
||||
}
|
||||
|
||||
// 渲染相关商品
|
||||
function renderRelatedProducts(products) {
|
||||
const lang = i18n.currentLang;
|
||||
|
||||
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;
|
||||
|
||||
return `
|
||||
<div class="product-card">
|
||||
<a href="product-detail.html?id=${product.id}">
|
||||
<div class="product-image">
|
||||
<img src="${productImage}" alt="${productName}" loading="lazy">
|
||||
</div>
|
||||
<div class="product-info">
|
||||
<h3 class="product-title">${productName}</h3>
|
||||
<div class="product-price">
|
||||
<span class="price-current">¥${price.toFixed(2)}</span>
|
||||
${originalPrice ? `<span class="price-original">¥${originalPrice.toFixed(2)}</span>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
|
||||
$('#relatedProducts').html(productsHtml);
|
||||
}
|
||||
|
||||
// 绑定事件
|
||||
function bindEvents() {
|
||||
// 缩略图点击
|
||||
$(document).on('click', '.thumbnail', function() {
|
||||
const index = $(this).data('index');
|
||||
const imgSrc = $(this).find('img').attr('src');
|
||||
$('#mainImage').attr('src', imgSrc);
|
||||
$('.thumbnail').removeClass('active');
|
||||
$(this).addClass('active');
|
||||
});
|
||||
|
||||
// 数量增减
|
||||
$('.qty-btn.minus').on('click', function() {
|
||||
const $input = $('#quantityInput');
|
||||
let qty = parseInt($input.val()) || 1;
|
||||
if (qty > 1) {
|
||||
$input.val(qty - 1);
|
||||
}
|
||||
});
|
||||
|
||||
$('.qty-btn.plus').on('click', function() {
|
||||
const $input = $('#quantityInput');
|
||||
let qty = parseInt($input.val()) || 1;
|
||||
const max = parseInt($input.attr('max')) || 99;
|
||||
if (qty < max) {
|
||||
$input.val(qty + 1);
|
||||
}
|
||||
});
|
||||
|
||||
$('#quantityInput').on('change', function() {
|
||||
let qty = parseInt($(this).val()) || 1;
|
||||
const max = parseInt($(this).attr('max')) || 99;
|
||||
if (qty < 1) qty = 1;
|
||||
if (qty > max) qty = max;
|
||||
$(this).val(qty);
|
||||
});
|
||||
|
||||
// 规格选择
|
||||
$(document).on('click', '.spec-option', function() {
|
||||
const $option = $(this);
|
||||
const specIndex = $option.data('spec-index');
|
||||
|
||||
// 取消同组其他选项
|
||||
$(`.spec-option[data-spec-index="${specIndex}"]`).removeClass('selected');
|
||||
// 选中当前选项
|
||||
$option.addClass('selected');
|
||||
|
||||
// 更新选中的SKU
|
||||
updateSelectedSku();
|
||||
});
|
||||
|
||||
// 评论标签切换
|
||||
$(document).on('click', '.comment-tab', function() {
|
||||
const $tab = $(this);
|
||||
const filter = $tab.data('filter');
|
||||
|
||||
$('.comment-tab').removeClass('active');
|
||||
$tab.addClass('active');
|
||||
|
||||
currentCommentFilter = filter;
|
||||
currentCommentPage = 1;
|
||||
|
||||
const productId = getProductIdFromUrl();
|
||||
loadComments(productId, 1, filter);
|
||||
});
|
||||
|
||||
// 评论分页
|
||||
$(document).on('click', '#commentPagination .page-btn', function() {
|
||||
const page = parseInt($(this).data('page'));
|
||||
const productId = getProductIdFromUrl();
|
||||
|
||||
currentCommentPage = page;
|
||||
loadComments(productId, page, currentCommentFilter);
|
||||
|
||||
// 滚动到评论区域
|
||||
$('html, body').animate({
|
||||
scrollTop: $('#reviews').offset().top - 100
|
||||
}, 300);
|
||||
});
|
||||
|
||||
// Tab切换
|
||||
$(document).on('click', '.tab-header', function() {
|
||||
const $tab = $(this);
|
||||
const tabId = $tab.data('tab');
|
||||
|
||||
$('.tab-header').removeClass('active');
|
||||
$tab.addClass('active');
|
||||
|
||||
$('.tab-content').removeClass('active');
|
||||
$(`#${tabId}`).addClass('active');
|
||||
});
|
||||
|
||||
// 立即购买
|
||||
$('#buyNowBtn').on('click', function() {
|
||||
if (!currentProduct || !currentProduct.isStock) {
|
||||
Toast.error('商品缺货');
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果有规格,检查是否已选择
|
||||
if (currentProduct.specList && currentProduct.specList.length > 0) {
|
||||
const selectedCount = $('.spec-option.selected').length;
|
||||
if (selectedCount < currentProduct.specList.length) {
|
||||
// 找到未选择的规格组
|
||||
const unselectedSpecs = [];
|
||||
currentProduct.specList.forEach((spec, index) => {
|
||||
const hasSelected = $(`.spec-option.selected[data-spec-index="${index}"]`).length > 0;
|
||||
if (!hasSelected) {
|
||||
const specName = spec.title || spec.specName || spec.name || `规格${index + 1}`;
|
||||
unselectedSpecs.push(specName);
|
||||
// 高亮未选择的规格组
|
||||
$(`.spec-options[data-spec-index="${index}"]`).addClass('spec-required');
|
||||
setTimeout(() => {
|
||||
$(`.spec-options[data-spec-index="${index}"]`).removeClass('spec-required');
|
||||
}, 2000);
|
||||
}
|
||||
});
|
||||
Toast.error(`请选择${unselectedSpecs.join('、')}`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const quantity = parseInt($('#quantityInput').val()) || 1;
|
||||
|
||||
// 先加入购物车,然后跳转结算
|
||||
addToCart(quantity, true);
|
||||
});
|
||||
|
||||
// 加入购物车
|
||||
$('#addToCartBtn').on('click', function() {
|
||||
if (!currentProduct || !currentProduct.isStock) {
|
||||
Toast.error('商品缺货');
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果有规格,检查是否已选择
|
||||
if (currentProduct.specList && currentProduct.specList.length > 0) {
|
||||
const selectedCount = $('.spec-option.selected').length;
|
||||
if (selectedCount < currentProduct.specList.length) {
|
||||
// 找到未选择的规格组
|
||||
const unselectedSpecs = [];
|
||||
currentProduct.specList.forEach((spec, index) => {
|
||||
const hasSelected = $(`.spec-option.selected[data-spec-index="${index}"]`).length > 0;
|
||||
if (!hasSelected) {
|
||||
const specName = spec.title || spec.specName || spec.name || `规格${index + 1}`;
|
||||
unselectedSpecs.push(specName);
|
||||
// 高亮未选择的规格组
|
||||
$(`.spec-options[data-spec-index="${index}"]`).addClass('spec-required');
|
||||
setTimeout(() => {
|
||||
$(`.spec-options[data-spec-index="${index}"]`).removeClass('spec-required');
|
||||
}, 2000);
|
||||
}
|
||||
});
|
||||
Toast.error(`请选择${unselectedSpecs.join('、')}`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const quantity = parseInt($('#quantityInput').val()) || 1;
|
||||
addToCart(quantity, false);
|
||||
});
|
||||
}
|
||||
|
||||
// 更新选中的SKU
|
||||
function updateSelectedSku() {
|
||||
if (!currentProduct || !currentProduct.skuList || currentProduct.skuList.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取所有选中的规格值
|
||||
const selectedSpecs = [];
|
||||
$('.spec-option.selected').each(function() {
|
||||
selectedSpecs.push($(this).data('spec-value'));
|
||||
});
|
||||
|
||||
// 查找匹配的SKU
|
||||
const sku = currentProduct.skuList.find(s => {
|
||||
if (!s.specInfo || s.specInfo.length !== selectedSpecs.length) {
|
||||
return false;
|
||||
}
|
||||
return s.specInfo.every(spec => selectedSpecs.includes(spec.specValue));
|
||||
});
|
||||
|
||||
if (sku) {
|
||||
selectedSku = sku;
|
||||
// 更新价格显示
|
||||
const price = PriceUtils.fenToYuan(sku.priceInfo && sku.priceInfo[0] ? sku.priceInfo[0].price : 0);
|
||||
$('#currentPrice').text(`¥${price.toFixed(2)}`);
|
||||
|
||||
// 更新库存
|
||||
const stock = sku.stockQuantity || 0;
|
||||
if (stock > 0) {
|
||||
$('#stockStatus').text('有货').addClass('in-stock').removeClass('out-of-stock');
|
||||
$('#quantity').attr('max', stock);
|
||||
} else {
|
||||
$('#stockStatus').text('缺货').addClass('out-of-stock').removeClass('in-stock');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 加入购物车
|
||||
function addToCart(quantity, buyNow = false) {
|
||||
// 如果是立即购买,直接跳转到结算页,不需要加入购物车
|
||||
if (buyNow) {
|
||||
const productId = currentProduct.spuId || currentProduct.id;
|
||||
const skuId = selectedSku ? (selectedSku.skuId || selectedSku.id) : '';
|
||||
|
||||
// 构建 URL 参数
|
||||
const params = new URLSearchParams({
|
||||
type: 'buynow',
|
||||
product_id: productId,
|
||||
quantity: quantity
|
||||
});
|
||||
|
||||
if (skuId) {
|
||||
params.append('sku_id', skuId);
|
||||
}
|
||||
|
||||
// 直接跳转到结算页
|
||||
window.location.href = `checkout.html?${params.toString()}`;
|
||||
return;
|
||||
}
|
||||
|
||||
// 普通加入购物车流程
|
||||
const data = {
|
||||
product_id: parseInt(currentProduct.spuId || currentProduct.id),
|
||||
quantity: quantity
|
||||
};
|
||||
|
||||
// 如果选择了SKU
|
||||
if (selectedSku) {
|
||||
data.sku_id = parseInt(selectedSku.skuId || selectedSku.id);
|
||||
}
|
||||
|
||||
console.log('加入购物车:', data);
|
||||
|
||||
API.post('/cart', data)
|
||||
.then(() => {
|
||||
Toast.success('已添加到购物车');
|
||||
loadCartCount();
|
||||
|
||||
// 打开购物车抽屉
|
||||
setTimeout(() => {
|
||||
if (typeof window.openCartDrawer === 'function') {
|
||||
window.openCartDrawer();
|
||||
}
|
||||
}, 300);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('添加到购物车失败:', error);
|
||||
Toast.error(error.message || '添加失败');
|
||||
});
|
||||
}
|
||||
|
||||
// 加载购物车数量
|
||||
function 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);
|
||||
});
|
||||
}
|
||||
|
||||
// 加载评论统计
|
||||
function loadCommentsCount(productId) {
|
||||
API.get(`/comments/products/${productId}/stats`)
|
||||
.then(data => {
|
||||
const totalCount = data.total_count || 0;
|
||||
const avgRating = data.average_rating || 0;
|
||||
const goodCount = (data.rating_4_count || 0) + (data.rating_5_count || 0);
|
||||
const mediumCount = data.rating_3_count || 0;
|
||||
const badCount = (data.rating_1_count || 0) + (data.rating_2_count || 0);
|
||||
const imageCount = data.has_images_count || 0;
|
||||
|
||||
// 更新评论数量显示
|
||||
$('#reviewCount').text(`${totalCount} reviews`);
|
||||
|
||||
// 更新星级显示
|
||||
renderStars(avgRating);
|
||||
|
||||
// 更新评论标签
|
||||
updateCommentTabs(totalCount, goodCount, mediumCount, badCount, imageCount);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('加载评论统计失败:', error);
|
||||
});
|
||||
}
|
||||
|
||||
// 渲染星级
|
||||
function renderStars(rating) {
|
||||
const fullStars = Math.floor(rating);
|
||||
const hasHalfStar = rating % 1 >= 0.5;
|
||||
let starsHtml = '';
|
||||
|
||||
for (let i = 0; i < 5; i++) {
|
||||
if (i < fullStars) {
|
||||
starsHtml += '★';
|
||||
} else if (i === fullStars && hasHalfStar) {
|
||||
starsHtml += '☆';
|
||||
} else {
|
||||
starsHtml += '☆';
|
||||
}
|
||||
}
|
||||
|
||||
$('#productStars').html(starsHtml);
|
||||
}
|
||||
|
||||
// 更新评论标签
|
||||
function updateCommentTabs(total, good, medium, bad, image) {
|
||||
const tabsHtml = `
|
||||
<div class="comment-tabs">
|
||||
<button class="comment-tab active" data-filter="all">全部 (${total})</button>
|
||||
<button class="comment-tab" data-filter="good">好评 (${good})</button>
|
||||
<button class="comment-tab" data-filter="medium">中评 (${medium})</button>
|
||||
<button class="comment-tab" data-filter="bad">差评 (${bad})</button>
|
||||
<button class="comment-tab" data-filter="image">有图 (${image})</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
$('#reviewsList').before(tabsHtml);
|
||||
}
|
||||
|
||||
// 加载评论列表
|
||||
function loadComments(productId, page = 1, filter = 'all') {
|
||||
const params = {
|
||||
page: page,
|
||||
page_size: 10
|
||||
};
|
||||
|
||||
// 根据筛选条件添加参数
|
||||
if (filter === 'good') {
|
||||
params.rating = 4; // 4-5星
|
||||
} else if (filter === 'medium') {
|
||||
params.rating = 3; // 3星
|
||||
} else if (filter === 'bad') {
|
||||
params.rating = 2; // 1-2星
|
||||
} else if (filter === 'image') {
|
||||
params.has_images = true;
|
||||
}
|
||||
|
||||
API.get(`/comments/products/${productId}`, params)
|
||||
.then(data => {
|
||||
const comments = data.list || [];
|
||||
renderComments(comments);
|
||||
|
||||
// 更新分页
|
||||
renderCommentPagination(data.page, data.total, data.page_size);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('加载评论失败:', error);
|
||||
$('#reviewsList').html('<div class="empty-reviews">暂无评论</div>');
|
||||
});
|
||||
}
|
||||
|
||||
// 渲染评论列表
|
||||
function renderComments(comments) {
|
||||
if (comments.length === 0) {
|
||||
$('#reviewsList').html('<div class="empty-reviews">暂无评论</div>');
|
||||
return;
|
||||
}
|
||||
|
||||
const commentsHtml = comments.map(comment => {
|
||||
const userName = comment.user_name || '匿名用户';
|
||||
const userAvatar = comment.user_avatar || 'https://picsum.photos/40/40?random=' + comment.id;
|
||||
const rating = comment.rating || 5;
|
||||
const content = comment.content || '';
|
||||
const images = comment.images || [];
|
||||
const createdAt = formatDate(comment.created_at);
|
||||
const productSpec = comment.product_spec || '';
|
||||
const replyContent = comment.reply_content || '';
|
||||
|
||||
// 星级
|
||||
let starsHtml = '';
|
||||
for (let i = 0; i < 5; i++) {
|
||||
starsHtml += i < rating ? '★' : '☆';
|
||||
}
|
||||
|
||||
// 图片
|
||||
let imagesHtml = '';
|
||||
if (images.length > 0) {
|
||||
imagesHtml = `
|
||||
<div class="comment-images">
|
||||
${images.map(img => `
|
||||
<img src="${img}" alt="评论图片" class="comment-image" onclick="previewImage('${img}')">
|
||||
`).join('')}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// 商家回复
|
||||
let replyHtml = '';
|
||||
if (replyContent) {
|
||||
replyHtml = `
|
||||
<div class="merchant-reply">
|
||||
<div class="reply-label">商家回复:</div>
|
||||
<div class="reply-content">${replyContent}</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
return `
|
||||
<div class="review-item">
|
||||
<div class="review-header">
|
||||
<div class="review-author-info">
|
||||
<img src="${userAvatar}" alt="${userName}" class="review-avatar">
|
||||
<div>
|
||||
<div class="review-author-name">${userName}</div>
|
||||
${productSpec ? `<div class="review-spec">${productSpec}</div>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
<div class="review-meta">
|
||||
<div class="review-rating">${starsHtml}</div>
|
||||
<div class="review-date">${createdAt}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="review-content">
|
||||
<p class="review-text">${content}</p>
|
||||
${imagesHtml}
|
||||
</div>
|
||||
${replyHtml}
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
|
||||
$('#reviewsList').html(commentsHtml);
|
||||
}
|
||||
|
||||
// 渲染评论分页
|
||||
function renderCommentPagination(currentPage, total, pageSize) {
|
||||
const totalPages = Math.ceil(total / pageSize);
|
||||
|
||||
if (totalPages <= 1) {
|
||||
$('#commentPagination').hide();
|
||||
return;
|
||||
}
|
||||
|
||||
let paginationHtml = `<div class="pagination">`;
|
||||
|
||||
// 上一页
|
||||
if (currentPage > 1) {
|
||||
paginationHtml += `<button class="page-btn" data-page="${currentPage - 1}">上一页</button>`;
|
||||
}
|
||||
|
||||
// 页码
|
||||
for (let i = 1; i <= Math.min(totalPages, 5); i++) {
|
||||
const active = i === currentPage ? 'active' : '';
|
||||
paginationHtml += `<button class="page-btn ${active}" data-page="${i}">${i}</button>`;
|
||||
}
|
||||
|
||||
// 下一页
|
||||
if (currentPage < totalPages) {
|
||||
paginationHtml += `<button class="page-btn" data-page="${currentPage + 1}">下一页</button>`;
|
||||
}
|
||||
|
||||
paginationHtml += `</div>`;
|
||||
|
||||
$('#commentPagination').html(paginationHtml).show();
|
||||
}
|
||||
|
||||
// 格式化日期
|
||||
function formatDate(dateStr) {
|
||||
if (!dateStr) return '';
|
||||
|
||||
const date = new Date(dateStr);
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
|
||||
return `${year}-${month}-${day}`;
|
||||
}
|
||||
|
||||
// 图片预览
|
||||
function previewImage(imgUrl) {
|
||||
// 创建预览模态框
|
||||
const modal = `
|
||||
<div class="image-preview-modal" onclick="this.remove()">
|
||||
<img src="${imgUrl}" alt="预览图片">
|
||||
</div>
|
||||
`;
|
||||
$('body').append(modal);
|
||||
}
|
||||
Reference in New Issue
Block a user