详细说明: - 修复getProjectInfo函数中seq.agent()的类型错误 - 添加兼容性处理,支持函数和对象两种数据格式 - 解决选择订单班后点击按钮导致页面崩溃的问题 - 修改文件: WorkflowPageV4.tsx (第934行) - 影响模块: ResultModal数据显示系统 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
490 lines
16 KiB
JavaScript
490 lines
16 KiB
JavaScript
// 同里古镇农文旅宣传片策划案 - 主要JavaScript逻辑
|
|
|
|
// 页面初始化
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
console.log('🎬 视觉设计订单班同里宣传片策划页面加载完成');
|
|
|
|
// 初始化Lucide图标
|
|
if (typeof lucide !== 'undefined') {
|
|
lucide.createIcons();
|
|
}
|
|
|
|
// 初始化组件
|
|
initNavigation();
|
|
initAnimations();
|
|
initLazyLoading();
|
|
initSmoothScroll();
|
|
updateStats();
|
|
initThemeToggle();
|
|
initAccordion();
|
|
initComparisonSlider();
|
|
initImageLightbox();
|
|
});
|
|
|
|
// 导航功能
|
|
function initNavigation() {
|
|
const navItems = document.querySelectorAll('.nav-item');
|
|
const sections = document.querySelectorAll('.section');
|
|
|
|
// 点击导航项滚动到对应区块
|
|
navItems.forEach((item, index) => {
|
|
item.addEventListener('click', () => {
|
|
// 移除所有活跃状态
|
|
navItems.forEach(nav => nav.classList.remove('active'));
|
|
// 添加当前活跃状态
|
|
item.classList.add('active');
|
|
|
|
// 滚动到对应区块
|
|
if (sections[index]) {
|
|
const targetSection = sections[index];
|
|
const offsetTop = targetSection.offsetTop - 100;
|
|
|
|
window.scrollTo({
|
|
top: offsetTop,
|
|
behavior: 'smooth'
|
|
});
|
|
}
|
|
});
|
|
});
|
|
|
|
// 滚动时更新导航活跃状态
|
|
window.addEventListener('scroll', () => {
|
|
let current = '';
|
|
sections.forEach((section, index) => {
|
|
const sectionTop = section.offsetTop - 150;
|
|
if (scrollY >= sectionTop) {
|
|
current = index;
|
|
}
|
|
});
|
|
|
|
navItems.forEach((item, index) => {
|
|
item.classList.remove('active');
|
|
if (index === current) {
|
|
item.classList.add('active');
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
// 动画初始化
|
|
function initAnimations() {
|
|
// 使用 Intersection Observer 实现滚动动画
|
|
const observerOptions = {
|
|
root: null,
|
|
rootMargin: '0px',
|
|
threshold: 0.1
|
|
};
|
|
|
|
const observer = new IntersectionObserver((entries) => {
|
|
entries.forEach(entry => {
|
|
if (entry.isIntersecting) {
|
|
entry.target.classList.add('fade-in');
|
|
observer.unobserve(entry.target);
|
|
}
|
|
});
|
|
}, observerOptions);
|
|
|
|
// 观察所有需要动画的元素
|
|
const animatedElements = document.querySelectorAll('.card, .expert-intro');
|
|
animatedElements.forEach(el => {
|
|
el.style.opacity = '0';
|
|
observer.observe(el);
|
|
});
|
|
}
|
|
|
|
// 图片懒加载
|
|
function initLazyLoading() {
|
|
const images = document.querySelectorAll('img[data-src]');
|
|
|
|
const imageObserver = new IntersectionObserver((entries, observer) => {
|
|
entries.forEach(entry => {
|
|
if (entry.isIntersecting) {
|
|
const img = entry.target;
|
|
const src = img.getAttribute('data-src');
|
|
|
|
// 创建新图片对象来预加载
|
|
const tempImg = new Image();
|
|
tempImg.onload = function() {
|
|
img.src = src;
|
|
img.classList.add('loaded');
|
|
};
|
|
tempImg.onerror = function() {
|
|
// 如果图片加载失败,使用琥珀色主题占位图
|
|
img.src = 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" width="400" height="300" viewBox="0 0 400 300"%3E%3Crect width="400" height="300" fill="%23fef3c7"/%3E%3Ctext x="50%25" y="50%25" dominant-baseline="middle" text-anchor="middle" font-family="system-ui" font-size="20" fill="%23d97706"%3E图片加载中%3C/text%3E%3C/svg%3E';
|
|
img.classList.add('error');
|
|
};
|
|
tempImg.src = src;
|
|
|
|
img.removeAttribute('data-src');
|
|
observer.unobserve(img);
|
|
}
|
|
});
|
|
});
|
|
|
|
images.forEach(img => {
|
|
imageObserver.observe(img);
|
|
});
|
|
}
|
|
|
|
// 平滑滚动
|
|
function initSmoothScroll() {
|
|
// 为所有锚点链接添加平滑滚动
|
|
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
|
anchor.addEventListener('click', function (e) {
|
|
e.preventDefault();
|
|
const targetId = this.getAttribute('href');
|
|
if (targetId === '#') return;
|
|
|
|
const target = document.querySelector(targetId);
|
|
if (target) {
|
|
target.scrollIntoView({
|
|
behavior: 'smooth',
|
|
block: 'start'
|
|
});
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
// 更新统计数据 - 影视拍摄相关数据
|
|
function updateStats() {
|
|
// 动画数字增长效果 - 影视拍摄相关数据
|
|
const stats = [
|
|
{ selector: '.shooting-days', value: 7, suffix: '天' },
|
|
{ selector: '.shot-count', value: 17, suffix: '个' },
|
|
{ selector: '.team-members', value: 12, suffix: '人' },
|
|
{ selector: '.budget-amount', value: 27.38, suffix: '万' },
|
|
{ selector: '.video-duration', value: 6.5, suffix: '分钟' },
|
|
{ selector: '.filming-sections', value: 6, suffix: '个' }
|
|
];
|
|
|
|
stats.forEach(stat => {
|
|
const element = document.querySelector(stat.selector);
|
|
if (element) {
|
|
animateValue(element, 0, stat.value, 2000, stat.suffix);
|
|
}
|
|
});
|
|
}
|
|
|
|
// 数字动画函数
|
|
function animateValue(element, start, end, duration, suffix = '') {
|
|
const startTime = performance.now();
|
|
|
|
function update(currentTime) {
|
|
const elapsed = currentTime - startTime;
|
|
const progress = Math.min(elapsed / duration, 1);
|
|
|
|
// 使用缓动函数
|
|
const easeOutQuad = progress * (2 - progress);
|
|
let current;
|
|
|
|
// 处理小数值
|
|
if (end < 10 && end % 1 !== 0) {
|
|
current = (start + (end - start) * easeOutQuad).toFixed(1);
|
|
} else {
|
|
current = Math.floor(start + (end - start) * easeOutQuad);
|
|
}
|
|
|
|
element.textContent = current + suffix;
|
|
|
|
if (progress < 1) {
|
|
requestAnimationFrame(update);
|
|
}
|
|
}
|
|
|
|
requestAnimationFrame(update);
|
|
}
|
|
|
|
// 错误处理
|
|
window.addEventListener('error', function(e) {
|
|
if (e.target.tagName === 'IMG') {
|
|
console.warn('图片加载失败:', e.target.src);
|
|
e.target.src = 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" width="400" height="300" viewBox="0 0 400 300"%3E%3Crect width="400" height="300" fill="%23fef3c7"/%3E%3Ctext x="50%25" y="50%25" dominant-baseline="middle" text-anchor="middle" font-family="system-ui" font-size="20" fill="%23d97706"%3E图片暂时无法显示%3C/text%3E%3C/svg%3E';
|
|
e.target.classList.add('error');
|
|
}
|
|
}, true);
|
|
|
|
// 移动端优化
|
|
if ('ontouchstart' in window) {
|
|
document.body.classList.add('touch-device');
|
|
|
|
// 移动端点击优化
|
|
let touchStartTime;
|
|
document.addEventListener('touchstart', () => {
|
|
touchStartTime = Date.now();
|
|
});
|
|
|
|
document.addEventListener('touchend', (e) => {
|
|
const touchEndTime = Date.now();
|
|
if (touchEndTime - touchStartTime < 200) {
|
|
// 快速点击
|
|
e.preventDefault();
|
|
}
|
|
});
|
|
}
|
|
|
|
// 性能监控
|
|
if (window.performance && window.performance.timing) {
|
|
window.addEventListener('load', () => {
|
|
setTimeout(() => {
|
|
const timing = window.performance.timing;
|
|
const loadTime = timing.loadEventEnd - timing.navigationStart;
|
|
console.log(`页面加载时间: ${loadTime}ms`);
|
|
|
|
// 如果加载时间过长,提示用户
|
|
if (loadTime > 3000) {
|
|
console.warn('页面加载时间较长,可能需要优化');
|
|
}
|
|
}, 0);
|
|
});
|
|
}
|
|
|
|
// 主题切换功能
|
|
function initThemeToggle() {
|
|
const themeToggleBtn = document.getElementById('themeToggleBtn');
|
|
|
|
// 从localStorage读取用户的主题偏好
|
|
const savedTheme = localStorage.getItem('theme');
|
|
// 如果没有保存的偏好,默认使用暗色主题
|
|
if (savedTheme === 'dark' || savedTheme === null) {
|
|
document.body.classList.add('dark-theme');
|
|
}
|
|
|
|
// 点击切换主题
|
|
if (themeToggleBtn) {
|
|
themeToggleBtn.addEventListener('click', () => {
|
|
document.body.classList.toggle('dark-theme');
|
|
|
|
// 保存用户偏好
|
|
if (document.body.classList.contains('dark-theme')) {
|
|
localStorage.setItem('theme', 'dark');
|
|
} else {
|
|
localStorage.setItem('theme', 'light');
|
|
}
|
|
|
|
// 重新初始化图标以确保正确显示
|
|
if (typeof lucide !== 'undefined') {
|
|
lucide.createIcons();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
// 手风琴面板交互逻辑
|
|
function initAccordion() {
|
|
const accordionHeaders = document.querySelectorAll('.accordion-header');
|
|
|
|
accordionHeaders.forEach(header => {
|
|
header.addEventListener('click', () => {
|
|
const accordionItem = header.parentElement;
|
|
const wasActive = accordionItem.classList.contains('active');
|
|
|
|
// 关闭所有其他面板
|
|
document.querySelectorAll('.accordion-item').forEach(otherItem => {
|
|
otherItem.classList.remove('active');
|
|
});
|
|
|
|
// 切换当前面板状态
|
|
if (!wasActive) {
|
|
accordionItem.classList.add('active');
|
|
}
|
|
|
|
// 重新初始化Lucide图标以确保折叠箭头正确显示
|
|
if (typeof lucide !== 'undefined') {
|
|
lucide.createIcons();
|
|
}
|
|
});
|
|
});
|
|
|
|
// 默认打开第一个面板(预算明细)
|
|
const firstAccordionItem = document.querySelector('.accordion-item');
|
|
if (firstAccordionItem && !firstAccordionItem.classList.contains('active')) {
|
|
firstAccordionItem.classList.add('active');
|
|
}
|
|
}
|
|
|
|
// 图片对比滑块交互逻辑
|
|
function initComparisonSlider() {
|
|
const comparisonContainers = document.querySelectorAll('.comparison-container');
|
|
|
|
comparisonContainers.forEach(container => {
|
|
const slider = container.querySelector('.comparison-slider');
|
|
const afterImage = container.querySelector('.comparison-image-after');
|
|
|
|
let isDragging = false;
|
|
|
|
// 更新滑块位置
|
|
function updatePosition(clientX) {
|
|
const rect = container.getBoundingClientRect();
|
|
const x = clientX - rect.left;
|
|
const percentage = Math.max(0, Math.min(100, (x / rect.width) * 100));
|
|
|
|
container.style.setProperty('--position', `${percentage}%`);
|
|
}
|
|
|
|
// 鼠标事件
|
|
function handleMouseDown(e) {
|
|
isDragging = true;
|
|
container.style.cursor = 'ew-resize';
|
|
updatePosition(e.clientX);
|
|
e.preventDefault();
|
|
}
|
|
|
|
function handleMouseMove(e) {
|
|
if (!isDragging) return;
|
|
updatePosition(e.clientX);
|
|
}
|
|
|
|
function handleMouseUp() {
|
|
isDragging = false;
|
|
container.style.cursor = 'ew-resize';
|
|
}
|
|
|
|
// 触摸事件
|
|
function handleTouchStart(e) {
|
|
isDragging = true;
|
|
updatePosition(e.touches[0].clientX);
|
|
e.preventDefault();
|
|
}
|
|
|
|
function handleTouchMove(e) {
|
|
if (!isDragging) return;
|
|
updatePosition(e.touches[0].clientX);
|
|
e.preventDefault();
|
|
}
|
|
|
|
function handleTouchEnd() {
|
|
isDragging = false;
|
|
}
|
|
|
|
// 绑定事件监听器
|
|
container.addEventListener('mousedown', handleMouseDown);
|
|
document.addEventListener('mousemove', handleMouseMove);
|
|
document.addEventListener('mouseup', handleMouseUp);
|
|
|
|
container.addEventListener('touchstart', handleTouchStart, { passive: false });
|
|
document.addEventListener('touchmove', handleTouchMove, { passive: false });
|
|
document.addEventListener('touchend', handleTouchEnd);
|
|
|
|
// 点击直接跳转到点击位置
|
|
container.addEventListener('click', (e) => {
|
|
if (!isDragging) {
|
|
updatePosition(e.clientX);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
// 图片放大Lightbox功能
|
|
function initImageLightbox() {
|
|
// 创建Lightbox模态框HTML结构
|
|
const lightboxHTML = `
|
|
<div class="lightbox-modal" id="lightboxModal">
|
|
<div class="lightbox-content">
|
|
<img src="" alt="放大图片" class="lightbox-image" id="lightboxImage">
|
|
</div>
|
|
<div class="lightbox-close" id="lightboxClose"></div>
|
|
<div class="lightbox-label" id="lightboxLabel"></div>
|
|
</div>
|
|
`;
|
|
|
|
// 将Lightbox添加到页面底部
|
|
document.body.insertAdjacentHTML('beforeend', lightboxHTML);
|
|
|
|
const lightboxModal = document.getElementById('lightboxModal');
|
|
const lightboxImage = document.getElementById('lightboxImage');
|
|
const lightboxLabel = document.getElementById('lightboxLabel');
|
|
const lightboxClose = document.getElementById('lightboxClose');
|
|
|
|
// 获取所有对比图片
|
|
const comparisonImages = document.querySelectorAll('.comparison-image');
|
|
|
|
// 跟踪拖动状态
|
|
let dragStartTime = 0;
|
|
let dragStartX = 0;
|
|
let dragStartY = 0;
|
|
|
|
comparisonImages.forEach(img => {
|
|
// 记录鼠标按下位置和时间
|
|
img.addEventListener('mousedown', (e) => {
|
|
dragStartTime = Date.now();
|
|
dragStartX = e.clientX;
|
|
dragStartY = e.clientY;
|
|
});
|
|
|
|
img.addEventListener('touchstart', (e) => {
|
|
dragStartTime = Date.now();
|
|
dragStartX = e.touches[0].clientX;
|
|
dragStartY = e.touches[0].clientY;
|
|
}, { passive: true });
|
|
|
|
// 点击图片打开Lightbox
|
|
img.addEventListener('click', (e) => {
|
|
const dragDuration = Date.now() - dragStartTime;
|
|
const dragDistanceX = Math.abs(e.clientX - dragStartX);
|
|
const dragDistanceY = Math.abs(e.clientY - dragStartY);
|
|
const dragDistance = Math.sqrt(dragDistanceX * dragDistanceX + dragDistanceY * dragDistanceY);
|
|
|
|
// 只有快速点击且移动距离小于10px才打开Lightbox
|
|
if (dragDuration < 200 && dragDistance < 10) {
|
|
openLightbox(img);
|
|
e.stopPropagation(); // 阻止事件冒泡到comparison-container
|
|
}
|
|
});
|
|
});
|
|
|
|
// 打开Lightbox
|
|
function openLightbox(img) {
|
|
// 获取图片源(已经通过懒加载完成)
|
|
const imgSrc = img.src || img.getAttribute('data-src');
|
|
|
|
// 如果图片还未加载,等待加载
|
|
if (!img.src || img.src.includes('data:image/svg+xml')) {
|
|
console.warn('图片尚未加载完成');
|
|
return;
|
|
}
|
|
|
|
// 判断是"生成图"还是"线稿"
|
|
const isAfter = img.classList.contains('comparison-image-after');
|
|
const labelText = isAfter ? '线稿' : '生成图';
|
|
|
|
// 设置图片和标签
|
|
lightboxImage.src = imgSrc;
|
|
lightboxLabel.textContent = labelText;
|
|
|
|
// 显示模态框
|
|
lightboxModal.classList.add('active');
|
|
|
|
// 禁止页面滚动
|
|
document.body.style.overflow = 'hidden';
|
|
}
|
|
|
|
// 关闭Lightbox
|
|
function closeLightbox() {
|
|
lightboxModal.classList.remove('active');
|
|
|
|
// 恢复页面滚动
|
|
document.body.style.overflow = '';
|
|
}
|
|
|
|
// 关闭按钮点击事件
|
|
lightboxClose.addEventListener('click', (e) => {
|
|
closeLightbox();
|
|
e.stopPropagation();
|
|
});
|
|
|
|
// 点击遮罩层关闭(点击图片本身不关闭)
|
|
lightboxModal.addEventListener('click', (e) => {
|
|
if (e.target === lightboxModal) {
|
|
closeLightbox();
|
|
}
|
|
});
|
|
|
|
// ESC键关闭
|
|
document.addEventListener('keydown', (e) => {
|
|
if (e.key === 'Escape' && lightboxModal.classList.contains('active')) {
|
|
closeLightbox();
|
|
}
|
|
});
|
|
}
|