Files
advertising-system/script.js
Snowz 9e8a88274d perf(页面加载): 优化价格显示性能并添加加载状态处理
- 改用DOMContentLoaded事件提前初始化页面
- 实现渐进式价格显示:基础价格→加载中→实时价格
- 为fetchVisitData添加超时和重试机制
- 新增价格状态视觉区分样式
- 从2分钟等待优化为立即显示基础价格,后台异步更新
2025-08-17 14:49:06 +08:00

521 lines
17 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.

// 广告位配置
const AD_POSITIONS = {
'home-banner-1': {
id: 'home-banner-1',
title: '首页顶部Banner广告位1',
image: 'https://www.zmt.wiki',
dimensions: '1200×200px',
prices: {
monthly: 250,
yearly: 500
}
},
'home-banner-2': {
id: 'home-banner-2',
title: '首页顶部Banner广告位2',
image: 'https://www.zmt.wiki',
dimensions: '1200×200px',
prices: {
monthly: 250,
yearly: 500
}
},
'home-sidebar': {
id: 'home-sidebar',
title: '首页侧边栏广告位',
image: 'https://www.zmt.wiki',
dimensions: '300×600px',
prices: {
yearly: 300
}
},
'category-top': {
id: 'category-top',
title: '分类页置顶广告位',
image: 'https://www.zmt.wiki',
dimensions: '800×150px',
prices: {
monthly: 100
}
},
'article-end': {
id: 'article-end',
title: '文末广告位',
image: 'https://www.zmt.wiki',
dimensions: '600×300px',
prices: {
yearly: 200
}
},
'url-top': {
id: 'url-top',
title: '网址置顶推荐',
image: 'https://www.zmt.wiki',
dimensions: '800×100px',
prices: {
monthly: 200,
yearly: 500
}
}
};
// 访问量数据从API获取
let visitData = {
today_uv: 0,
today_pv: 0,
yesterday_uv: 0,
yesterday_pv: 0,
last_month_pv: 0,
last_year_pv: 0
};
// 访问量价格系数配置
const VISIT_COEFFICIENTS = {
'low': { min: 0, max: 30000, coefficient: 0.8 }, // 低访问量价格打8折
'medium': { min: 30001, max: 60000, coefficient: 1 }, // 中等访问量,原价
'high': { min: 60001, max: 100000, coefficient: 1.2 }, // 高访问量价格上浮20%
'veryHigh': { min: 100001, max: Infinity, coefficient: 1.5 } // 极高访问量价格上浮50%
};
// 广告位预订状态(实际项目中应该从数据库获取)
const BOOKED_STATUS = {
'home-banner-1': {
isBooked: false,
endDate: null
},
'home-banner-2': {
isBooked: false,
endDate: null
},
'home-sidebar': {
isBooked: true,
endDate: new Date('2026-12-31').toISOString()
},
'category-top': {
isBooked: false,
endDate: null
},
'article-end': {
isBooked: false,
endDate: null
},
'url-top': {
isBooked: false,
endDate: null
}
};
// 生成广告位HTML
function generateAdCardHtml(adId, adConfig) {
const status = BOOKED_STATUS[adId];
const isBooked = status.isBooked;
const endDate = status.endDate ? new Date(status.endDate) : null;
let statusHtml = '<span class="status-available">可预订</span>';
if (isBooked && endDate) {
const formattedDate = endDate.toLocaleDateString('zh-CN', {
year: 'numeric',
month: 'long',
day: 'numeric'
});
statusHtml = `
<span class="status-booked">
已预订
<span class="end-date">到期时间:${formattedDate}</span>
</span>
`;
}
let priceHtml = '';
if (adConfig.prices.monthly) {
priceHtml += `<p>月付价格:<span id="${adId}-monthly">计算中...</span> 元/月</p>`;
}
if (adConfig.prices.yearly) {
priceHtml += `<p>年付价格:<span id="${adId}-yearly">计算中...</span> 元/年</p>`;
}
let bookingButtons = '';
if (!isBooked) {
if (adConfig.prices.monthly && adConfig.prices.yearly) {
bookingButtons = `
<div class="booking-options">
<button class="book-btn" onclick="bookAd('${adId}', 'monthly')">月付预订</button>
<button class="book-btn" onclick="bookAd('${adId}', 'yearly')">年付预订</button>
</div>
`;
} else if (adConfig.prices.monthly) {
bookingButtons = `<button class="book-btn" onclick="bookAd('${adId}', 'monthly')">月付预订</button>`;
} else if (adConfig.prices.yearly) {
bookingButtons = `<button class="book-btn" onclick="bookAd('${adId}', 'yearly')">年付预订</button>`;
}
}
return `
<div class="ad-card" id="${adId}">
<h2>${adConfig.title}</h2>
<div class="ad-preview">
<a href="${adConfig.image}" target="_blank">
<img src="${adConfig.image}" alt="${adConfig.title}预览" class="preview-img">
<div class="preview-overlay">
<span class="preview-hint">点击查看大图</span>
<div class="ad-dimensions">尺寸:${adConfig.dimensions}</div>
</div>
</a>
</div>
<div class="ad-stats">
${priceHtml}
</div>
<div class="ad-status" id="${adId}-status">
${statusHtml}
</div>
${bookingButtons}
</div>
`;
}
// 初始化广告位
function initAdPositions() {
const adPositionsContainer = document.querySelector('.ad-positions');
if (!adPositionsContainer) return;
let html = '';
Object.entries(AD_POSITIONS).forEach(([adId, adConfig]) => {
html += generateAdCardHtml(adId, adConfig);
});
adPositionsContainer.innerHTML = html;
}
// 获取访问量数据
/**
* 获取访问量数据(带超时和重试机制)
* @param {number} timeout 超时时间(毫秒)
* @param {number} retries 重试次数
* @returns {Promise<boolean>} 是否成功获取数据
*/
async function fetchVisitData(timeout = 10000, retries = 2) {
for (let attempt = 0; attempt <= retries; attempt++) {
try {
console.log(`正在获取访问量数据... (尝试 ${attempt + 1}/${retries + 1})`);
// 创建带超时的fetch请求
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
const response = await fetch('info.php', {
signal: controller.signal,
cache: 'no-cache'
});
clearTimeout(timeoutId);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
// 验证数据完整性
if (data && typeof data.last_month_pv === 'number') {
visitData = data;
console.log('访问量数据获取成功:', visitData);
return true;
} else {
throw new Error('返回数据格式不正确');
}
} catch (error) {
console.warn(`获取访问量数据失败 (尝试 ${attempt + 1}/${retries + 1}):`, error.message);
// 如果是最后一次尝试,记录错误但不抛出异常
if (attempt === retries) {
console.error('所有重试均失败,将使用基础价格');
return false;
}
// 等待一段时间后重试
await new Promise(resolve => setTimeout(resolve, 1000 * (attempt + 1)));
}
}
return false;
}
// 根据访问量获取价格系数
function getPriceCoefficient(monthlyPV) {
for (const [key, range] of Object.entries(VISIT_COEFFICIENTS)) {
if (monthlyPV >= range.min && monthlyPV <= range.max) {
return range.coefficient;
}
}
return 1; // 默认系数
}
// 计算实际价格
function calculateActualPrice(basePrice, monthlyPV) {
const coefficient = getPriceCoefficient(monthlyPV);
return Math.round(basePrice * coefficient);
}
/**
* 显示基础价格不依赖API数据
*/
function showBasePrices() {
console.log('显示基础价格...');
Object.entries(AD_POSITIONS).forEach(([adId, adConfig]) => {
const prices = adConfig.prices;
// 显示月付基础价格
if (prices.monthly) {
const monthlyElement = document.getElementById(`${adId}-monthly`);
if (monthlyElement) {
monthlyElement.textContent = prices.monthly.toLocaleString();
monthlyElement.classList.add('base-price');
}
}
// 显示年付基础价格
if (prices.yearly) {
const yearlyElement = document.getElementById(`${adId}-yearly`);
if (yearlyElement) {
yearlyElement.textContent = prices.yearly.toLocaleString();
yearlyElement.classList.add('base-price');
}
}
});
}
/**
* 显示价格加载状态
*/
function showPriceLoading() {
console.log('显示价格加载状态...');
Object.entries(AD_POSITIONS).forEach(([adId, adConfig]) => {
const prices = adConfig.prices;
if (prices.monthly) {
const monthlyElement = document.getElementById(`${adId}-monthly`);
if (monthlyElement) {
monthlyElement.innerHTML = '<span class="loading-price">计算中...</span>';
}
}
if (prices.yearly) {
const yearlyElement = document.getElementById(`${adId}-yearly`);
if (yearlyElement) {
yearlyElement.innerHTML = '<span class="loading-price">计算中...</span>';
}
}
});
}
/**
* 更新所有广告位的价格显示(实时价格)
*/
function updateAllAdPrices() {
console.log('开始更新实时价格...');
try {
Object.entries(AD_POSITIONS).forEach(([adId, adConfig]) => {
const prices = adConfig.prices;
// 更新月付价格显示
if (prices.monthly) {
const monthlyElement = document.getElementById(`${adId}-monthly`);
if (monthlyElement) {
const actualPrice = calculateActualPrice(prices.monthly, visitData.last_month_pv);
monthlyElement.textContent = actualPrice.toLocaleString();
monthlyElement.classList.remove('base-price', 'loading-price');
monthlyElement.classList.add('real-time-price');
}
}
// 更新年付价格显示
if (prices.yearly) {
const yearlyElement = document.getElementById(`${adId}-yearly`);
if (yearlyElement) {
const actualPrice = calculateActualPrice(prices.yearly, visitData.last_month_pv);
yearlyElement.textContent = actualPrice.toLocaleString();
yearlyElement.classList.remove('base-price', 'loading-price');
yearlyElement.classList.add('real-time-price');
}
}
});
console.log('实时价格更新完成');
} catch (error) {
console.error('更新价格时出错:', error);
// 如果更新失败,回退到基础价格
showBasePrices();
}
}
// 处理广告预订
function bookAd(adPosition, paymentType) {
const status = BOOKED_STATUS[adPosition];
if (status.isBooked) {
alert('该广告位已被预订,请选择其他广告位或等待到期后再预订。');
return;
}
const adConfig = AD_POSITIONS[adPosition];
const basePrice = adConfig.prices[paymentType];
if (!basePrice) {
alert('该广告位不支持此支付方式');
return;
}
const actualPrice = calculateActualPrice(basePrice, visitData.last_month_pv);
const duration = paymentType === 'yearly' ? 365 : 30;
const confirmMessage = `您选择的广告位:${adConfig.title}\n支付方式:${paymentType === 'yearly' ? '年付' : '月付'}\n总价格:${actualPrice}\n\n是否确认预订?`;
if (confirm(confirmMessage)) {
// 计算到期时间
const endDate = new Date();
endDate.setDate(endDate.getDate() + duration);
// 更新预订状态
BOOKED_STATUS[adPosition] = {
isBooked: true,
endDate: endDate.toISOString()
};
// 重新生成广告位卡片
const adCard = document.getElementById(adPosition);
if (adCard) {
adCard.outerHTML = generateAdCardHtml(adPosition, adConfig);
}
alert('预订成功!我们的客服人员将尽快与您联系。');
}
}
// 图片预览功能
function initImagePreview() {
const modal = document.getElementById('previewModal');
const modalImg = modal.querySelector('.preview-modal-img');
const modalTitle = modal.querySelector('.preview-modal-title');
const modalDimensions = modal.querySelector('.preview-modal-dimensions');
const closeBtn = modal.querySelector('.preview-close');
// 确保模态框初始状态正确
modal.style.display = 'none';
// 点击预览图打开模态框
document.querySelectorAll('.preview-img').forEach(img => {
img.addEventListener('click', function() {
const card = this.closest('.ad-card');
const title = card.querySelector('h2').textContent;
const dimensions = card.querySelector('.ad-dimensions').textContent;
modalImg.src = this.src;
modalTitle.textContent = title;
modalDimensions.textContent = dimensions;
// 显示模态框
modal.style.display = 'flex';
setTimeout(() => {
modal.classList.add('show');
}, 10);
// 禁用滚动
document.body.style.overflow = 'hidden';
});
});
// 点击关闭按钮
closeBtn.addEventListener('click', function() {
modal.classList.remove('show');
setTimeout(() => {
modal.style.display = 'none';
document.body.style.overflow = '';
}, 300);
});
// 点击模态框背景关闭
modal.addEventListener('click', function(e) {
if (e.target === modal) {
modal.classList.remove('show');
setTimeout(() => {
modal.style.display = 'none';
document.body.style.overflow = '';
}, 300);
}
});
// 按ESC键关闭
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape' && modal.classList.contains('show')) {
modal.classList.remove('show');
setTimeout(() => {
modal.style.display = 'none';
document.body.style.overflow = '';
}, 300);
}
});
}
// 处理联系表单提交
document.querySelector('.contact-form').addEventListener('submit', function(e) {
e.preventDefault();
const formData = new FormData(this);
const data = Object.fromEntries(formData.entries());
// 这里可以添加表单验证逻辑
// 模拟表单提交
alert('感谢您的咨询!我们的客服人员将尽快与您联系。');
this.reset();
});
// 确保页面完全加载后再执行
/**
* 页面初始化函数
*/
async function initializePage() {
console.log('开始初始化页面...');
// 1. 立即初始化广告位和显示基础价格
initAdPositions();
showBasePrices();
console.log('基础价格显示完成');
// 2. 显示加载状态并异步获取实时数据
setTimeout(() => {
showPriceLoading();
// 异步获取访问量数据并更新价格
fetchVisitData(15000, 3).then(success => {
if (success) {
updateAllAdPrices();
} else {
// 如果获取失败,回退到基础价格
console.log('API获取失败显示基础价格');
showBasePrices();
}
});
}, 100); // 短暂延迟确保基础价格先显示
// 3. 设置定期更新每5分钟
setInterval(async () => {
console.log('定期更新访问量数据...');
const success = await fetchVisitData(10000, 1);
if (success) {
updateAllAdPrices();
}
}, 5 * 60 * 1000);
}
// 使用DOMContentLoaded而不是window.onload提前初始化
document.addEventListener('DOMContentLoaded', function() {
console.log('DOM加载完成开始初始化...');
initializePage();
});
// 保留window.onload作为备用防止DOMContentLoaded未触发
window.addEventListener('load', function() {
// 检查是否已经初始化过
if (!document.querySelector('.ad-positions').hasChildNodes()) {
console.log('备用初始化触发...');
initializePage();
}
});