详细说明: - 修复WorkflowPageV3.tsx中的TypeScript类型错误 - 移除未使用的executionTimeoutRef变量 - 修复style标签的jsx属性问题 - 将deprecated的substr()改为substring() - 清理n8n目录下的副本文件 - 添加server.js和start脚本用于静态文件服务 影响的文件: - web_frontend/exhibition-demo/src/pages/WorkflowPageV3.tsx - web_frontend/exhibition-demo/src/components/ResultModal.tsx - web_frontend/web_result/server.js (新增) - web_frontend/web_result/start.bat (新增) - web_frontend/web_result/start.sh (新增) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
709 lines
22 KiB
JavaScript
709 lines
22 KiB
JavaScript
// 2024长三角国际新能源汽车与智能交通产业博览会 - 交互脚本
|
||
|
||
// 页面加载完成后初始化
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
// 隐藏页面加载器
|
||
hidePageLoader();
|
||
|
||
initNavbar();
|
||
initAnimations();
|
||
initCounters();
|
||
initScrollEffects();
|
||
initRippleEffect();
|
||
initLazyLoading();
|
||
initFormValidation();
|
||
initCharts();
|
||
initTimeline();
|
||
initInteractiveElements();
|
||
handleImageErrors();
|
||
});
|
||
|
||
// 隐藏页面加载器
|
||
function hidePageLoader() {
|
||
const loader = document.getElementById('pageLoader');
|
||
if (loader) {
|
||
// 添加淡出动画
|
||
loader.style.opacity = '0';
|
||
loader.style.transition = 'opacity 0.5s ease-out';
|
||
|
||
// 500ms后完全移除
|
||
setTimeout(() => {
|
||
loader.style.display = 'none';
|
||
// 恢复body滚动
|
||
document.body.style.overflow = 'auto';
|
||
document.body.classList.remove('loading');
|
||
}, 500);
|
||
} else {
|
||
// 如果没有找到加载器,也要确保恢复滚动
|
||
document.body.style.overflow = 'auto';
|
||
document.body.classList.remove('loading');
|
||
}
|
||
}
|
||
|
||
// 如果页面加载超过3秒,强制隐藏加载器
|
||
window.addEventListener('load', function() {
|
||
setTimeout(() => {
|
||
hidePageLoader();
|
||
}, 3000);
|
||
});
|
||
|
||
// 导航栏交互
|
||
function initNavbar() {
|
||
const navbar = document.querySelector('.navbar');
|
||
const navLinks = document.querySelectorAll('.nav-link');
|
||
const mobileMenuBtn = document.querySelector('.mobile-menu-btn');
|
||
const mobileMenu = document.querySelector('.mobile-menu');
|
||
|
||
// 滚动时改变导航栏样式
|
||
window.addEventListener('scroll', () => {
|
||
if (window.scrollY > 100) {
|
||
navbar?.classList.add('scrolled');
|
||
} else {
|
||
navbar?.classList.remove('scrolled');
|
||
}
|
||
});
|
||
|
||
// 高亮当前页面链接
|
||
const currentPath = window.location.pathname;
|
||
navLinks.forEach(link => {
|
||
if (link.getAttribute('href') === currentPath) {
|
||
link.classList.add('active');
|
||
}
|
||
});
|
||
|
||
// 移动端菜单切换
|
||
mobileMenuBtn?.addEventListener('click', () => {
|
||
mobileMenu?.classList.toggle('open');
|
||
mobileMenuBtn.classList.toggle('active');
|
||
});
|
||
|
||
// 平滑滚动到锚点
|
||
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
||
anchor.addEventListener('click', function(e) {
|
||
e.preventDefault();
|
||
const target = document.querySelector(this.getAttribute('href'));
|
||
if (target) {
|
||
target.scrollIntoView({
|
||
behavior: 'smooth',
|
||
block: 'start'
|
||
});
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
// 动画初始化
|
||
function initAnimations() {
|
||
// Intersection Observer for fade-in animations
|
||
const observerOptions = {
|
||
threshold: 0.1,
|
||
rootMargin: '0px 0px -50px 0px'
|
||
};
|
||
|
||
const observer = new IntersectionObserver((entries) => {
|
||
entries.forEach(entry => {
|
||
if (entry.isIntersecting) {
|
||
entry.target.classList.add('animate-in');
|
||
observer.unobserve(entry.target);
|
||
}
|
||
});
|
||
}, observerOptions);
|
||
|
||
// 观察所有需要动画的元素
|
||
document.querySelectorAll('.fade-in, .slide-in-left, .slide-in-right, .grid-item').forEach(el => {
|
||
observer.observe(el);
|
||
});
|
||
}
|
||
|
||
// 数字计数器动画
|
||
function initCounters() {
|
||
const counters = document.querySelectorAll('.counter');
|
||
const speed = 200; // 动画速度
|
||
|
||
const countUp = (counter) => {
|
||
const target = +counter.getAttribute('data-target');
|
||
const increment = target / speed;
|
||
|
||
const updateCount = () => {
|
||
const count = +counter.innerText.replace(/[^0-9]/g, '');
|
||
|
||
if (count < target) {
|
||
counter.innerText = Math.ceil(count + increment).toLocaleString();
|
||
setTimeout(updateCount, 1);
|
||
} else {
|
||
counter.innerText = target.toLocaleString();
|
||
|
||
// 添加单位
|
||
const unit = counter.getAttribute('data-unit');
|
||
if (unit) {
|
||
counter.innerText += unit;
|
||
}
|
||
}
|
||
};
|
||
|
||
updateCount();
|
||
};
|
||
|
||
// 使用 Intersection Observer 触发计数
|
||
const counterObserver = new IntersectionObserver((entries) => {
|
||
entries.forEach(entry => {
|
||
if (entry.isIntersecting) {
|
||
countUp(entry.target);
|
||
counterObserver.unobserve(entry.target);
|
||
}
|
||
});
|
||
}, { threshold: 0.5 });
|
||
|
||
counters.forEach(counter => {
|
||
counter.innerText = '0';
|
||
counterObserver.observe(counter);
|
||
});
|
||
}
|
||
|
||
// 滚动效果
|
||
function initScrollEffects() {
|
||
let lastScrollTop = 0;
|
||
const header = document.querySelector('header');
|
||
|
||
window.addEventListener('scroll', () => {
|
||
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
|
||
|
||
// 隐藏/显示导航栏
|
||
if (scrollTop > lastScrollTop && scrollTop > 300) {
|
||
header?.classList.add('hide');
|
||
} else {
|
||
header?.classList.remove('hide');
|
||
}
|
||
|
||
lastScrollTop = scrollTop <= 0 ? 0 : scrollTop;
|
||
|
||
// 视差效果
|
||
const parallaxElements = document.querySelectorAll('.parallax');
|
||
parallaxElements.forEach(el => {
|
||
const speed = el.getAttribute('data-speed') || 0.5;
|
||
const yPos = -(scrollTop * speed);
|
||
el.style.transform = `translateY(${yPos}px)`;
|
||
});
|
||
|
||
// 进度条
|
||
const progressBar = document.querySelector('.progress-bar');
|
||
if (progressBar) {
|
||
const winScroll = document.body.scrollTop || document.documentElement.scrollTop;
|
||
const height = document.documentElement.scrollHeight - document.documentElement.clientHeight;
|
||
const scrolled = (winScroll / height) * 100;
|
||
progressBar.style.width = scrolled + '%';
|
||
}
|
||
});
|
||
}
|
||
|
||
// 波纹效果
|
||
function initRippleEffect() {
|
||
document.querySelectorAll('.btn, .card').forEach(element => {
|
||
element.addEventListener('click', function(e) {
|
||
const ripple = document.createElement('span');
|
||
ripple.classList.add('ripple');
|
||
|
||
const rect = this.getBoundingClientRect();
|
||
const size = Math.max(rect.width, rect.height);
|
||
const x = e.clientX - rect.left - size / 2;
|
||
const y = e.clientY - rect.top - size / 2;
|
||
|
||
ripple.style.width = ripple.style.height = size + 'px';
|
||
ripple.style.left = x + 'px';
|
||
ripple.style.top = y + 'px';
|
||
|
||
this.appendChild(ripple);
|
||
|
||
setTimeout(() => {
|
||
ripple.remove();
|
||
}, 600);
|
||
});
|
||
});
|
||
}
|
||
|
||
// 懒加载
|
||
function initLazyLoading() {
|
||
const images = document.querySelectorAll('img[data-src]');
|
||
|
||
const imageObserver = new IntersectionObserver((entries) => {
|
||
entries.forEach(entry => {
|
||
if (entry.isIntersecting) {
|
||
const img = entry.target;
|
||
img.src = img.dataset.src;
|
||
img.removeAttribute('data-src');
|
||
imageObserver.unobserve(img);
|
||
|
||
// 添加加载完成动画
|
||
img.addEventListener('load', () => {
|
||
img.classList.add('loaded');
|
||
});
|
||
}
|
||
});
|
||
}, {
|
||
rootMargin: '50px 0px'
|
||
});
|
||
|
||
images.forEach(img => imageObserver.observe(img));
|
||
}
|
||
|
||
// 表单验证
|
||
function initFormValidation() {
|
||
const forms = document.querySelectorAll('form');
|
||
|
||
forms.forEach(form => {
|
||
form.addEventListener('submit', function(e) {
|
||
e.preventDefault();
|
||
|
||
let isValid = true;
|
||
const inputs = form.querySelectorAll('input[required], textarea[required]');
|
||
|
||
inputs.forEach(input => {
|
||
if (!input.value.trim()) {
|
||
isValid = false;
|
||
input.classList.add('error');
|
||
showError(input, '此字段为必填项');
|
||
} else {
|
||
input.classList.remove('error');
|
||
clearError(input);
|
||
}
|
||
|
||
// 邮箱验证
|
||
if (input.type === 'email' && input.value) {
|
||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||
if (!emailRegex.test(input.value)) {
|
||
isValid = false;
|
||
input.classList.add('error');
|
||
showError(input, '请输入有效的邮箱地址');
|
||
}
|
||
}
|
||
|
||
// 电话验证
|
||
if (input.type === 'tel' && input.value) {
|
||
const phoneRegex = /^1[3-9]\d{9}$/;
|
||
if (!phoneRegex.test(input.value)) {
|
||
isValid = false;
|
||
input.classList.add('error');
|
||
showError(input, '请输入有效的手机号码');
|
||
}
|
||
}
|
||
});
|
||
|
||
if (isValid) {
|
||
// 显示成功消息
|
||
showSuccess('表单提交成功!');
|
||
form.reset();
|
||
}
|
||
});
|
||
|
||
// 实时验证
|
||
form.querySelectorAll('input, textarea').forEach(input => {
|
||
input.addEventListener('blur', function() {
|
||
validateInput(this);
|
||
});
|
||
|
||
input.addEventListener('input', function() {
|
||
if (this.classList.contains('error')) {
|
||
validateInput(this);
|
||
}
|
||
});
|
||
});
|
||
});
|
||
}
|
||
|
||
// 显示错误信息
|
||
function showError(input, message) {
|
||
const errorEl = input.nextElementSibling;
|
||
if (errorEl && errorEl.classList.contains('error-message')) {
|
||
errorEl.textContent = message;
|
||
} else {
|
||
const error = document.createElement('span');
|
||
error.classList.add('error-message');
|
||
error.textContent = message;
|
||
input.parentNode.insertBefore(error, input.nextSibling);
|
||
}
|
||
}
|
||
|
||
// 清除错误信息
|
||
function clearError(input) {
|
||
const errorEl = input.nextElementSibling;
|
||
if (errorEl && errorEl.classList.contains('error-message')) {
|
||
errorEl.remove();
|
||
}
|
||
}
|
||
|
||
// 验证输入
|
||
function validateInput(input) {
|
||
if (input.hasAttribute('required') && !input.value.trim()) {
|
||
input.classList.add('error');
|
||
showError(input, '此字段为必填项');
|
||
return false;
|
||
}
|
||
|
||
input.classList.remove('error');
|
||
clearError(input);
|
||
return true;
|
||
}
|
||
|
||
// 显示成功消息
|
||
function showSuccess(message) {
|
||
const toast = document.createElement('div');
|
||
toast.classList.add('toast', 'success');
|
||
toast.textContent = message;
|
||
document.body.appendChild(toast);
|
||
|
||
setTimeout(() => {
|
||
toast.classList.add('show');
|
||
}, 100);
|
||
|
||
setTimeout(() => {
|
||
toast.classList.remove('show');
|
||
setTimeout(() => {
|
||
toast.remove();
|
||
}, 300);
|
||
}, 3000);
|
||
}
|
||
|
||
// 初始化图表
|
||
function initCharts() {
|
||
// 预算分配饼图
|
||
const budgetChart = document.getElementById('budgetChart');
|
||
if (budgetChart) {
|
||
const data = [
|
||
{ label: '场地租赁', value: 35, color: '#10b981' },
|
||
{ label: '营销推广', value: 25, color: '#3b82f6' },
|
||
{ label: '运营服务', value: 20, color: '#8b5cf6' },
|
||
{ label: '人员成本', value: 15, color: '#f59e0b' },
|
||
{ label: '其他费用', value: 5, color: '#ef4444' }
|
||
];
|
||
|
||
drawPieChart(budgetChart, data);
|
||
}
|
||
|
||
// 参展商类别分布
|
||
const exhibitorChart = document.getElementById('exhibitorChart');
|
||
if (exhibitorChart) {
|
||
const data = [
|
||
{ label: '整车制造', value: 40 },
|
||
{ label: '零部件', value: 30 },
|
||
{ label: '充电设施', value: 15 },
|
||
{ label: '智能驾驶', value: 10 },
|
||
{ label: '其他', value: 5 }
|
||
];
|
||
|
||
drawBarChart(exhibitorChart, data);
|
||
}
|
||
}
|
||
|
||
// 绘制饼图
|
||
function drawPieChart(canvas, data) {
|
||
const ctx = canvas.getContext('2d');
|
||
const centerX = canvas.width / 2;
|
||
const centerY = canvas.height / 2;
|
||
const radius = Math.min(centerX, centerY) - 20;
|
||
|
||
let currentAngle = -Math.PI / 2;
|
||
const total = data.reduce((sum, item) => sum + item.value, 0);
|
||
|
||
data.forEach(item => {
|
||
const sliceAngle = (item.value / total) * 2 * Math.PI;
|
||
|
||
// 绘制扇形
|
||
ctx.beginPath();
|
||
ctx.arc(centerX, centerY, radius, currentAngle, currentAngle + sliceAngle);
|
||
ctx.lineTo(centerX, centerY);
|
||
ctx.fillStyle = item.color;
|
||
ctx.fill();
|
||
|
||
// 绘制标签
|
||
const labelX = centerX + Math.cos(currentAngle + sliceAngle / 2) * (radius * 0.7);
|
||
const labelY = centerY + Math.sin(currentAngle + sliceAngle / 2) * (radius * 0.7);
|
||
|
||
ctx.fillStyle = '#fff';
|
||
ctx.font = '14px sans-serif';
|
||
ctx.textAlign = 'center';
|
||
ctx.fillText(item.value + '%', labelX, labelY);
|
||
|
||
currentAngle += sliceAngle;
|
||
});
|
||
}
|
||
|
||
// 绘制柱状图
|
||
function drawBarChart(canvas, data) {
|
||
const ctx = canvas.getContext('2d');
|
||
const barWidth = canvas.width / (data.length * 2);
|
||
const maxValue = Math.max(...data.map(item => item.value));
|
||
const chartHeight = canvas.height - 40;
|
||
|
||
data.forEach((item, index) => {
|
||
const barHeight = (item.value / maxValue) * chartHeight;
|
||
const x = (index * 2 + 0.5) * barWidth;
|
||
const y = canvas.height - barHeight - 20;
|
||
|
||
// 绘制柱子
|
||
const gradient = ctx.createLinearGradient(x, y, x, y + barHeight);
|
||
gradient.addColorStop(0, '#10b981');
|
||
gradient.addColorStop(1, '#3b82f6');
|
||
|
||
ctx.fillStyle = gradient;
|
||
ctx.fillRect(x, y, barWidth, barHeight);
|
||
|
||
// 绘制数值
|
||
ctx.fillStyle = '#333';
|
||
ctx.font = '12px sans-serif';
|
||
ctx.textAlign = 'center';
|
||
ctx.fillText(item.value + '%', x + barWidth / 2, y - 5);
|
||
});
|
||
}
|
||
|
||
// 初始化时间线
|
||
function initTimeline() {
|
||
const timelineItems = document.querySelectorAll('.timeline-item');
|
||
|
||
const timelineObserver = new IntersectionObserver((entries) => {
|
||
entries.forEach(entry => {
|
||
if (entry.isIntersecting) {
|
||
entry.target.classList.add('active');
|
||
}
|
||
});
|
||
}, { threshold: 0.5 });
|
||
|
||
timelineItems.forEach(item => {
|
||
timelineObserver.observe(item);
|
||
});
|
||
}
|
||
|
||
// 交互元素初始化
|
||
function initInteractiveElements() {
|
||
// 标签页切换
|
||
const tabs = document.querySelectorAll('.tab');
|
||
const tabContents = document.querySelectorAll('.tab-content');
|
||
|
||
tabs.forEach(tab => {
|
||
tab.addEventListener('click', () => {
|
||
const target = tab.dataset.tab;
|
||
|
||
// 切换标签状态
|
||
tabs.forEach(t => t.classList.remove('active'));
|
||
tab.classList.add('active');
|
||
|
||
// 切换内容显示
|
||
tabContents.forEach(content => {
|
||
if (content.id === target) {
|
||
content.classList.add('active');
|
||
content.style.display = 'block';
|
||
} else {
|
||
content.classList.remove('active');
|
||
content.style.display = 'none';
|
||
}
|
||
});
|
||
});
|
||
});
|
||
|
||
// 手风琴效果
|
||
const accordionHeaders = document.querySelectorAll('.accordion-header');
|
||
|
||
accordionHeaders.forEach(header => {
|
||
header.addEventListener('click', () => {
|
||
const content = header.nextElementSibling;
|
||
const isOpen = header.classList.contains('active');
|
||
|
||
// 关闭其他项
|
||
accordionHeaders.forEach(h => {
|
||
h.classList.remove('active');
|
||
h.nextElementSibling.style.maxHeight = null;
|
||
});
|
||
|
||
// 切换当前项
|
||
if (!isOpen) {
|
||
header.classList.add('active');
|
||
content.style.maxHeight = content.scrollHeight + 'px';
|
||
}
|
||
});
|
||
});
|
||
|
||
// 模态框
|
||
const modalTriggers = document.querySelectorAll('[data-modal]');
|
||
const modals = document.querySelectorAll('.modal');
|
||
const modalCloses = document.querySelectorAll('.modal-close');
|
||
|
||
modalTriggers.forEach(trigger => {
|
||
trigger.addEventListener('click', () => {
|
||
const modalId = trigger.dataset.modal;
|
||
const modal = document.getElementById(modalId);
|
||
if (modal) {
|
||
modal.classList.add('open');
|
||
document.body.style.overflow = 'hidden';
|
||
}
|
||
});
|
||
});
|
||
|
||
modalCloses.forEach(close => {
|
||
close.addEventListener('click', () => {
|
||
const modal = close.closest('.modal');
|
||
modal.classList.remove('open');
|
||
document.body.style.overflow = '';
|
||
});
|
||
});
|
||
|
||
// 点击背景关闭模态框
|
||
modals.forEach(modal => {
|
||
modal.addEventListener('click', (e) => {
|
||
if (e.target === modal) {
|
||
modal.classList.remove('open');
|
||
document.body.style.overflow = '';
|
||
}
|
||
});
|
||
});
|
||
|
||
// 工具提示
|
||
const tooltips = document.querySelectorAll('[data-tooltip]');
|
||
|
||
tooltips.forEach(element => {
|
||
const tooltip = document.createElement('div');
|
||
tooltip.classList.add('tooltip');
|
||
tooltip.textContent = element.dataset.tooltip;
|
||
|
||
element.addEventListener('mouseenter', () => {
|
||
document.body.appendChild(tooltip);
|
||
const rect = element.getBoundingClientRect();
|
||
tooltip.style.left = rect.left + rect.width / 2 - tooltip.offsetWidth / 2 + 'px';
|
||
tooltip.style.top = rect.top - tooltip.offsetHeight - 10 + 'px';
|
||
tooltip.classList.add('show');
|
||
});
|
||
|
||
element.addEventListener('mouseleave', () => {
|
||
tooltip.classList.remove('show');
|
||
setTimeout(() => {
|
||
tooltip.remove();
|
||
}, 300);
|
||
});
|
||
});
|
||
}
|
||
|
||
// 复制到剪贴板
|
||
function copyToClipboard(text) {
|
||
const textarea = document.createElement('textarea');
|
||
textarea.value = text;
|
||
document.body.appendChild(textarea);
|
||
textarea.select();
|
||
document.execCommand('copy');
|
||
document.body.removeChild(textarea);
|
||
showSuccess('已复制到剪贴板');
|
||
}
|
||
|
||
// 分享功能
|
||
function shareContent(platform) {
|
||
const url = encodeURIComponent(window.location.href);
|
||
const title = encodeURIComponent(document.title);
|
||
let shareUrl = '';
|
||
|
||
switch(platform) {
|
||
case 'wechat':
|
||
// 生成微信分享二维码
|
||
generateQRCode(window.location.href);
|
||
break;
|
||
case 'weibo':
|
||
shareUrl = `https://service.weibo.com/share/share.php?url=${url}&title=${title}`;
|
||
break;
|
||
case 'linkedin':
|
||
shareUrl = `https://www.linkedin.com/sharing/share-offsite/?url=${url}`;
|
||
break;
|
||
case 'twitter':
|
||
shareUrl = `https://twitter.com/intent/tweet?url=${url}&text=${title}`;
|
||
break;
|
||
}
|
||
|
||
if (shareUrl) {
|
||
window.open(shareUrl, '_blank', 'width=600,height=400');
|
||
}
|
||
}
|
||
|
||
// 生成二维码
|
||
function generateQRCode(text) {
|
||
const modal = document.createElement('div');
|
||
modal.classList.add('qr-modal');
|
||
modal.innerHTML = `
|
||
<div class="qr-content">
|
||
<h3>微信扫码分享</h3>
|
||
<div id="qrcode"></div>
|
||
<button class="btn btn-secondary" onclick="this.parentElement.parentElement.remove()">关闭</button>
|
||
</div>
|
||
`;
|
||
document.body.appendChild(modal);
|
||
|
||
// 这里可以集成实际的二维码生成库
|
||
document.getElementById('qrcode').innerHTML = '二维码生成区域';
|
||
}
|
||
|
||
// 主题切换
|
||
function initThemeToggle() {
|
||
const themeToggle = document.getElementById('themeToggle');
|
||
const currentTheme = localStorage.getItem('theme') || 'light';
|
||
|
||
document.documentElement.setAttribute('data-theme', currentTheme);
|
||
|
||
themeToggle?.addEventListener('click', () => {
|
||
const theme = document.documentElement.getAttribute('data-theme') === 'light' ? 'dark' : 'light';
|
||
document.documentElement.setAttribute('data-theme', theme);
|
||
localStorage.setItem('theme', theme);
|
||
});
|
||
}
|
||
|
||
// 导出为 PDF
|
||
function exportToPDF() {
|
||
window.print();
|
||
}
|
||
|
||
// 性能监控
|
||
function initPerformanceMonitoring() {
|
||
// 页面加载时间
|
||
window.addEventListener('load', () => {
|
||
const loadTime = performance.timing.loadEventEnd - performance.timing.navigationStart;
|
||
console.log(`页面加载时间: ${loadTime}ms`);
|
||
|
||
// 发送到分析服务
|
||
if (window.gtag) {
|
||
gtag('event', 'page_load_time', {
|
||
value: loadTime,
|
||
page_path: window.location.pathname
|
||
});
|
||
}
|
||
});
|
||
|
||
// 监控长任务
|
||
if ('PerformanceObserver' in window) {
|
||
const observer = new PerformanceObserver((list) => {
|
||
for (const entry of list.getEntries()) {
|
||
console.log('Long Task detected:', entry);
|
||
}
|
||
});
|
||
|
||
observer.observe({ entryTypes: ['longtask'] });
|
||
}
|
||
}
|
||
|
||
// 处理图片加载错误
|
||
function handleImageErrors() {
|
||
const images = document.querySelectorAll('img');
|
||
const defaultImage = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAwIiBoZWlnaHQ9IjMwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZTJlOGYwIi8+PHRleHQgeD0iNTAlIiB5PSI1MCUiIGZvbnQtZmFtaWx5PSJBcmlhbCIgZm9udC1zaXplPSIxOCIgZmlsbD0iIzk0YTNiOCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZHk9Ii4zZW0iPuWbvueJh+WKoOi9veS4rTwvdGV4dD48L3N2Zz4=';
|
||
|
||
images.forEach(img => {
|
||
// 添加错误处理
|
||
img.addEventListener('error', function() {
|
||
console.warn('图片加载失败:', this.src);
|
||
// 设置默认占位图
|
||
this.src = defaultImage;
|
||
this.alt = '图片加载失败';
|
||
this.classList.add('image-error');
|
||
});
|
||
|
||
// 添加加载成功处理
|
||
img.addEventListener('load', function() {
|
||
this.classList.add('image-loaded');
|
||
});
|
||
});
|
||
}
|
||
|
||
// 初始化性能监控
|
||
initPerformanceMonitoring();
|
||
initThemeToggle(); |