fc / apiSystem /templates /index /_js.html
jun2024's picture
Upload 2241 files
b4f2238 verified
Raw History Blame Contribute Delete
79 kB
<script> // Three.js Particle Background (function() { const canvas = document.getElementById('bg-canvas'); const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); const renderer = new THREE.WebGLRenderer({ canvas, alpha: true, antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); // Create particles const particlesGeometry = new THREE.BufferGeometry(); const particlesCount = 2000; const posArray = new Float32Array(particlesCount * 3); for(let i = 0; i < particlesCount * 3; i++) { posArray[i] = (Math.random() - 0.5) * 10; } particlesGeometry.setAttribute('position', new THREE.BufferAttribute(posArray, 3)); const particlesMaterial = new THREE.PointsMaterial({ size: 0.008, color: 0x6366f1, transparent: true, opacity: 0.8, blending: THREE.AdditiveBlending }); const particlesMesh = new THREE.Points(particlesGeometry, particlesMaterial); scene.add(particlesMesh); camera.position.z = 3; // Mouse interaction let mouseX = 0, mouseY = 0; document.addEventListener('mousemove', (e) => { mouseX = (e.clientX / window.innerWidth) * 2 - 1; mouseY = -(e.clientY / window.innerHeight) * 2 + 1; }); // Animation function animate() { requestAnimationFrame(animate); particlesMesh.rotation.x += 0.0003; particlesMesh.rotation.y += 0.0005; particlesMesh.rotation.x += mouseY * 0.0005; particlesMesh.rotation.y += mouseX * 0.0005; renderer.render(scene, camera); } animate(); // Resize handler window.addEventListener('resize', () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); })(); // GSAP Animations gsap.registerPlugin(ScrollTrigger); // Nav scroll effect window.addEventListener('scroll', () => { const nav = document.getElementById('nav'); if (window.scrollY > 50) { nav.classList.add('scrolled'); } else { nav.classList.remove('scrolled'); } }); // Reveal animations const reveals = document.querySelectorAll('.reveal'); reveals.forEach((el, i) => { gsap.fromTo(el, { opacity: 0, y: 40 }, { opacity: 1, y: 0, duration: 0.8, ease: 'power3.out', scrollTrigger: { trigger: el, start: 'top 85%', toggleActions: 'play none none none' }, delay: i * 0.1 } ); }); // 卡片入场动画 - 错落有致的 stagger 效果 const bentoCards = document.querySelectorAll('.bento-card'); bentoCards.forEach((card, index) => { // 计算错落延迟:根据卡片位置 const row = Math.floor(index / 4); const col = index % 4; const delay = (row * 0.15) + (col * 0.1); ScrollTrigger.create({ trigger: card, start: 'top 90%', onEnter: () => { setTimeout(() => { card.classList.add('animate-in'); }, delay * 1000); }, once: true }); }); // Counter animation - 增强版带弹跳后缀 const counters = document.querySelectorAll('.stat-value'); counters.forEach(counter => { const target = parseInt(counter.dataset.count); // 重构HTML结构 counter.innerHTML = '<span class="stat-number">0</span><span class="stat-suffix">+</span>'; const numberEl = counter.querySelector('.stat-number'); const suffix = counter.querySelector('.stat-suffix'); // 延迟启动动画(页面加载后) setTimeout(() => { gsap.to({ val: 0 }, { val: target, duration: 2, ease: 'power2.out', onUpdate: function() { numberEl.textContent = Math.round(this.targets()[0].val); }, onComplete: () => { // 数字完成后,后缀弹跳出现 suffix.classList.add('bounce'); } }); }, 1000); }); // Kinetic text effect const kineticText = document.querySelector('.kinetic-text'); if (kineticText) { kineticText.addEventListener('mouseenter', () => { gsap.to(kineticText, { scale: 1.05, duration: 0.3, ease: 'power2.out' }); }); kineticText.addEventListener('mouseleave', () => { gsap.to(kineticText, { scale: 1, duration: 0.3, ease: 'power2.out' }); }); } // Smooth scroll for nav links 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' }); } }); }); // ========== 震撼视觉效果 ========== // ========== 法院短信自动处理流程动画 ========== let flowAnimationRunning = false; let flowTimeline = null; function startFlowAnimation() { if (flowAnimationRunning) return; flowAnimationRunning = true; const playBtn = document.getElementById('flowPlayBtn'); playBtn.disabled = true; playBtn.innerHTML = '<span>⏸</span> 演示中...'; // 重置所有元素 resetFlowElements(); // 创建 GSAP 时间线 flowTimeline = gsap.timeline({ onComplete: () => { flowAnimationRunning = false; playBtn.disabled = false; playBtn.innerHTML = '<span>▶</span> 重新播放'; } }); // 步骤1: 手机出现,收到短信 flowTimeline .to('#phoneMockup', { opacity: 1, scale: 1, duration: 0.6, ease: 'back.out(1.7)' }) .call(() => updateFlowStep(1)) .to('#smsNotification', { opacity: 1, y: 0, duration: 0.4, ease: 'power2.out' }, '+=0.3') .call(() => { // 手机震动效果 gsap.to('#phoneMockup', { x: '+=3', duration: 0.05, repeat: 5, yoyo: true, ease: 'power2.inOut' }); }) // 步骤2: iOS快捷指令转发 .call(() => updateFlowStep(2), null, '+=0.8') .to('#smsForward', { opacity: 1, y: 0, duration: 0.4, ease: 'power2.out' }, '+=0.3') // 显示转发说明卡片 .to('#forwardCard', { opacity: 1, scale: 1, duration: 0.4, ease: 'back.out(1.7)' }, '+=0.2') .call(() => { document.getElementById('forwardCard').classList.add('active'); }) // 显示连接箭头 .to('#flowArrow1', { opacity: 1, duration: 0.3, ease: 'power2.out' }, '+=0.2') .call(() => { document.getElementById('flowArrow1').classList.add('active'); }) // 步骤3: 接收短信 .call(() => { updateFlowStep(3); setProcessNodeState('processReceive', 'processing'); }, null, '+=0.3') .call(() => { setProcessNodeState('processReceive', 'completed'); }, null, '+=0.5') // 解析链接 .call(() => { setProcessNodeState('processParse', 'processing'); }, null, '+=0.3') .call(() => { setProcessNodeState('processParse', 'completed'); }, null, '+=0.5') // 步骤4: 下载文书 .call(() => { updateFlowStep(4); setProcessNodeState('processDownload', 'processing'); }, null, '+=0.3') .call(() => { setProcessNodeState('processDownload', 'completed'); }, null, '+=0.7') // 步骤5: 匹配案件 + 重命名归档 .call(() => { updateFlowStep(5); setProcessNodeState('processMatch', 'processing'); }, null, '+=0.3') .call(() => { setProcessNodeState('processMatch', 'completed'); setProcessNodeState('processRename', 'processing'); }, null, '+=0.5') .call(() => { setProcessNodeState('processRename', 'completed'); }, null, '+=0.5') // 显示归档结果 .to('#resultBind', { opacity: 1, y: 0, scale: 1, duration: 0.4, ease: 'back.out(1.7)' }, '+=0.2') .to('#resultRename', { opacity: 1, y: 0, scale: 1, duration: 0.4, ease: 'back.out(1.7)' }, '+=0.15') // 步骤6: 智能分析 + 推送通知 .call(() => { updateFlowStep(6); setProcessNodeState('processAnalyze', 'processing'); }, null, '+=0.3') .call(() => { setProcessNodeState('processAnalyze', 'completed'); setProcessNodeState('processNotify', 'processing'); }, null, '+=0.6') .call(() => { setProcessNodeState('processNotify', 'completed'); }, null, '+=0.4') // 显示提醒和警告 .to('#resultReminder', { opacity: 1, y: 0, scale: 1, duration: 0.4, ease: 'back.out(1.7)' }, '+=0.2') .to('#resultAlert', { opacity: 1, y: 0, scale: 1, duration: 0.4, ease: 'back.out(1.7)' }, '+=0.2') // 完成所有步骤 .call(() => { document.querySelectorAll('.flow-step').forEach(step => { step.classList.remove('active'); step.classList.add('completed'); }); }, null, '+=0.5'); } function setProcessNodeState(id, state) { const el = document.getElementById(id); if (el) { el.classList.remove('active', 'processing', 'completed'); if (state) { el.classList.add(state); } } } function updateFlowStep(stepNum) { document.querySelectorAll('.flow-step').forEach(step => { const num = parseInt(step.dataset.step); step.classList.remove('active', 'completed'); if (num < stepNum) { step.classList.add('completed'); } else if (num === stepNum) { step.classList.add('active'); } }); } function resetFlowElements() { // 重置手机 gsap.set('#phoneMockup', { opacity: 0, scale: 0.9 }); gsap.set('#smsNotification', { opacity: 0, y: -20 }); gsap.set('#smsForward', { opacity: 0, y: -20 }); // 重置转发卡片和箭头 gsap.set('#forwardCard', { opacity: 0, scale: 0.9 }); gsap.set('#flowArrow1', { opacity: 0 }); const arrow = document.getElementById('flowArrow1'); if (arrow) arrow.classList.remove('active'); const card = document.getElementById('forwardCard'); if (card) card.classList.remove('active'); // 重置中间流程节点 document.querySelectorAll('.process-node').forEach(node => { node.classList.remove('active', 'processing', 'completed'); }); // 重置结果卡片 gsap.set('.result-card', { opacity: 0, y: 20, scale: 0.9 }); // 重置步骤指示器 document.querySelectorAll('.flow-step').forEach(step => { step.classList.remove('active', 'completed'); }); } function resetFlowAnimation() { if (flowTimeline) { flowTimeline.kill(); } flowAnimationRunning = false; resetFlowElements(); const playBtn = document.getElementById('flowPlayBtn'); playBtn.disabled = false; playBtn.innerHTML = '<span>▶</span> 播放演示'; } // 页面加载时初始化 document.addEventListener('DOMContentLoaded', () => { resetFlowElements(); // 当滚动到演示区域时自动播放 ScrollTrigger.create({ trigger: '#flowDemo', start: 'top 70%', onEnter: () => { if (!flowAnimationRunning) { setTimeout(startFlowAnimation, 500); } }, once: true }); }); // ========== 收件箱智能处理中枢动画 ========== let inboxFlowAnimationRunning = false; let inboxFlowTimeline = null; function setInboxStep(stepNum) { document.querySelectorAll('.inbox-flow-step').forEach(step => { const num = Number(step.dataset.step || '0'); step.classList.remove('active', 'completed'); if (num < stepNum) { step.classList.add('completed'); } else if (num === stepNum) { step.classList.add('active'); } }); } function activateInboxNode(id, className = 'active') { const el = document.getElementById(id); if (el) el.classList.add(className); } function setInboxText(id, text) { const el = document.getElementById(id); if (el) el.textContent = text; } function resetInboxFlowElements() { [ 'inboxAutoPollChip', 'inboxManualSyncChip', 'inboxParallelChip', 'inboxSyncStatus', 'inboxCourtMsg', 'inboxScannerFile', 'laneCourtIn', 'laneMaterialIn', 'laneCourtOut', 'laneMaterialOut', 'packetCourt', 'packetScanner', 'packetCourtOut', 'packetMaterialOut', 'inboxRouterCore', 'inboxFlowState', 'routeParse', 'routeClassify', 'routeMatch', 'routePolicy', 'inboxMetricCourt', 'inboxMetricMaterial', 'inboxMetricRisk', 'inboxBranchCourt', 'inboxBranchMaterial', 'inboxBranchIncoming', 'inboxBranchOutgoing', 'inboxCourtStep1', 'inboxCourtStep2', 'inboxCourtStep3', 'inboxIncomingStep1', 'inboxIncomingStep2', 'inboxIncomingStep3', 'inboxOutgoingStep1', 'inboxOutgoingStep2', 'inboxOutgoingStep3', 'inboxResultCard1', 'inboxResultCard2', 'inboxResultCard3', 'inboxResultCard4', 'inboxLog1', 'inboxLog2', 'inboxLog3', 'inboxLog4', 'inboxBranchCourtBadge', 'inboxBranchMaterialBadge' ].forEach(id => { const el = document.getElementById(id); if (!el) return; el.classList.remove('active', 'show', 'done'); }); setInboxText('inboxSyncStatus', '待同步 / Ready'); setInboxText('inboxFlowState', '待机 / Standby'); setInboxText('inboxBranchCourtBadge', '待命'); setInboxText('inboxBranchMaterialBadge', '待命'); setInboxText('inboxMetricCourtValue', '0%'); setInboxText('inboxMetricMaterialValue', '0'); setInboxText('inboxMetricRiskValue', '0'); setInboxText('inboxStatLinked', '0'); setInboxText('inboxStatSplit', '0'); setInboxText('inboxStatTodo', '0'); setInboxText('inboxStatFiled', '0'); document.querySelectorAll('.inbox-flow-step').forEach(step => { step.classList.remove('active', 'completed'); }); } function startInboxFlowAnimation() { if (inboxFlowAnimationRunning) return; inboxFlowAnimationRunning = true; const playBtn = document.getElementById('inboxFlowPlayBtn'); if (playBtn) { playBtn.disabled = true; playBtn.innerHTML = '<span>⏸</span> 演示中...'; } resetInboxFlowElements(); inboxFlowTimeline = gsap.timeline({ onComplete: () => { inboxFlowAnimationRunning = false; if (playBtn) { playBtn.disabled = false; playBtn.innerHTML = '<span>▶</span> 重新播放'; } document.querySelectorAll('.inbox-flow-step').forEach(step => { step.classList.remove('active'); step.classList.add('completed'); }); } }); inboxFlowTimeline .call(() => setInboxStep(1)) .call(() => { activateInboxNode('inboxAutoPollChip'); activateInboxNode('inboxManualSyncChip'); activateInboxNode('inboxParallelChip'); activateInboxNode('inboxFlowState'); setInboxText('inboxSyncStatus', '监听启动 / Listening'); setInboxText('inboxFlowState', '双线待命 / Parallel armed'); }, null, '+=0.2') .call(() => setInboxStep(2), null, '+=0.35') .call(() => { activateInboxNode('inboxCourtMsg'); activateInboxNode('inboxScannerFile'); activateInboxNode('laneCourtIn'); activateInboxNode('laneMaterialIn'); activateInboxNode('packetCourt'); activateInboxNode('packetScanner'); activateInboxNode('inboxRouterCore'); activateInboxNode('inboxSyncStatus'); setInboxText('inboxSyncStatus', '双线已入网 / Streams synced'); setInboxText('inboxFlowState', '流量接入 / Streams ingress'); }, null, '+=0.15') .call(() => setInboxStep(3), null, '+=0.3') .call(() => activateInboxNode('routeParse'), null, '+=0.12') .call(() => { activateInboxNode('routeClassify'); activateInboxNode('inboxMetricCourt'); setInboxText('inboxMetricCourtValue', '98%'); }, null, '+=0.12') .call(() => { activateInboxNode('routeMatch'); activateInboxNode('inboxMetricMaterial'); setInboxText('inboxMetricMaterialValue', '12'); }, null, '+=0.12') .call(() => { activateInboxNode('routePolicy'); activateInboxNode('inboxMetricRisk'); setInboxText('inboxMetricRiskValue', '1'); setInboxText('inboxFlowState', '解析完成 / Routing ready'); }, null, '+=0.12') .call(() => setInboxStep(4), null, '+=0.25') .call(() => { activateInboxNode('laneCourtOut'); activateInboxNode('packetCourtOut'); activateInboxNode('inboxBranchCourt'); setInboxText('inboxBranchCourtBadge', '处理中'); setInboxText('inboxFlowState', '法院线闭环 / Court branch'); }, null, '+=0.08') .call(() => activateInboxNode('inboxCourtStep1'), null, '+=0.15') .call(() => activateInboxNode('inboxCourtStep2'), null, '+=0.15') .call(() => activateInboxNode('inboxCourtStep3'), null, '+=0.15') .call(() => { activateInboxNode('inboxBranchCourtBadge', 'done'); setInboxText('inboxBranchCourtBadge', '已完成'); setInboxText('inboxStatLinked', '2'); }, null, '+=0.08') .call(() => setInboxStep(5), null, '+=0.22') .call(() => { activateInboxNode('laneMaterialOut'); activateInboxNode('packetMaterialOut'); activateInboxNode('inboxBranchMaterial'); activateInboxNode('inboxBranchIncoming'); activateInboxNode('inboxBranchOutgoing'); setInboxText('inboxBranchMaterialBadge', '处理中'); setInboxText('inboxFlowState', '材料闭环 / Material branch'); }, null, '+=0.08') .call(() => { activateInboxNode('inboxIncomingStep1'); activateInboxNode('inboxOutgoingStep1'); }, null, '+=0.12') .call(() => { activateInboxNode('inboxIncomingStep2'); activateInboxNode('inboxOutgoingStep2'); }, null, '+=0.14') .call(() => { activateInboxNode('inboxIncomingStep3'); activateInboxNode('inboxOutgoingStep3'); }, null, '+=0.14') .call(() => { activateInboxNode('inboxBranchMaterialBadge', 'done'); setInboxText('inboxBranchMaterialBadge', '已完成'); setInboxText('inboxStatSplit', '12'); setInboxText('inboxStatTodo', '3'); setInboxText('inboxStatFiled', '4'); }, null, '+=0.08') .call(() => setInboxStep(6), null, '+=0.22') .call(() => { setInboxText('inboxFlowState', '结果汇聚 / Closed loop'); setInboxText('inboxSyncStatus', '闭环完成 / Completed'); activateInboxNode('inboxResultCard1', 'show'); }) .call(() => activateInboxNode('inboxResultCard2', 'show'), null, '+=0.07') .call(() => activateInboxNode('inboxResultCard3', 'show'), null, '+=0.07') .call(() => activateInboxNode('inboxResultCard4', 'show'), null, '+=0.07') .call(() => activateInboxNode('inboxLog1', 'show'), null, '+=0.12') .call(() => activateInboxNode('inboxLog2', 'show'), null, '+=0.08') .call(() => activateInboxNode('inboxLog3', 'show'), null, '+=0.08') .call(() => activateInboxNode('inboxLog4', 'show'), null, '+=0.08'); } function resetInboxFlowAnimation() { if (inboxFlowTimeline) inboxFlowTimeline.kill(); inboxFlowAnimationRunning = false; resetInboxFlowElements(); const playBtn = document.getElementById('inboxFlowPlayBtn'); if (playBtn) { playBtn.disabled = false; playBtn.innerHTML = '<span>▶</span> 播放演示'; } } document.addEventListener('DOMContentLoaded', () => { resetInboxFlowElements(); ScrollTrigger.create({ trigger: '#inboxFlowDemo', start: 'top 70%', onEnter: () => { if (!inboxFlowAnimationRunning) { setTimeout(startInboxFlowAnimation, 450); } }, once: true }); }); // 1. 打字机效果 (function() { const heroTitle = document.querySelector('.hero-title'); if (!heroTitle) return; // 保存原始内容 const line1Text = '智能'; const line2Text = '法律案件'; const line3Text = '管理系统'; // 清空并重建结构 heroTitle.innerHTML = ` <span class="kinetic-text typewriter-line1"></span><span class="typewriter-line2"></span><br> <span class="gradient-text typewriter-line3"></span><span class="typewriter-cursor"></span> `; const line1El = heroTitle.querySelector('.typewriter-line1'); const line2El = heroTitle.querySelector('.typewriter-line2'); const line3El = heroTitle.querySelector('.typewriter-line3'); const cursor = heroTitle.querySelector('.typewriter-cursor'); let charIndex = 0; const fullText = line1Text + line2Text + line3Text; const speed = 120; // 每个字的间隔(ms) function typeWriter() { if (charIndex < fullText.length) { const char = fullText[charIndex]; if (charIndex < line1Text.length) { line1El.textContent += char; } else if (charIndex < line1Text.length + line2Text.length) { line2El.textContent += char; } else { line3El.textContent += char; } charIndex++; setTimeout(typeWriter, speed); } else { // 打字完成后光标继续闪烁 cursor.style.animation = 'cursorBlink 1s step-end infinite'; } } // 延迟启动打字机效果 setTimeout(typeWriter, 800); })(); // 2. 按钮悬停粒子效果 (function() { const buttons = document.querySelectorAll('.btn-primary'); buttons.forEach(btn => { btn.addEventListener('mouseenter', (e) => { // 创建小粒子 for (let i = 0; i < 6; i++) { const particle = document.createElement('span'); particle.style.cssText = ` position: absolute; width: 4px; height: 4px; background: white; border-radius: 50%; pointer-events: none; opacity: 0.8; `; btn.appendChild(particle); const rect = btn.getBoundingClientRect(); const startX = e.clientX - rect.left; const startY = e.clientY - rect.top; gsap.fromTo(particle, { x: startX, y: startY, scale: 1 }, { x: startX + (Math.random() - 0.5) * 80, y: startY + (Math.random() - 0.5) * 80, scale: 0, opacity: 0, duration: 0.6, ease: 'power2.out', onComplete: () => particle.remove() } ); } }); }); })(); // ========== 聊天记录梳理流程动画 - 紧凑版 ========== let chatFlowAnimationRunning = false; let chatFlowTimeline = null; function startChatFlowAnimation() { if (chatFlowAnimationRunning) return; chatFlowAnimationRunning = true; const playBtn = document.getElementById('chatFlowPlayBtn'); playBtn.disabled = true; playBtn.innerHTML = '<span>⏸</span> 演示中...'; // 重置所有元素 resetChatFlowElements(); // 创建 GSAP 时间线 chatFlowTimeline = gsap.timeline({ onComplete: () => { chatFlowAnimationRunning = false; playBtn.disabled = false; playBtn.innerHTML = '<span>▶</span> 重新播放'; } }); // ===== 步骤1: 上传视频 ===== chatFlowTimeline .to('#chatColUpload', { opacity: 1, scale: 1, duration: 0.4, ease: 'power2.out' }) .call(() => { document.getElementById('chatColUpload').classList.add('active'); }) // 模拟上传 .call(() => { const box = document.getElementById('chatUploadBox'); box.classList.add('uploading'); animateChatUploadProgress(); }, null, '+=0.3') .to({}, { duration: 1.2 }) // 等待上传 // 显示完成状态 .call(() => { document.getElementById('chatUploadBox').style.display = 'none'; document.getElementById('chatVideoDone').classList.add('show'); }) .to({}, { duration: 0.3 }) // 完成步骤1 .call(() => { document.getElementById('chatColUpload').classList.remove('active'); document.getElementById('chatColUpload').classList.add('completed'); }) // ===== 箭头1 ===== .to('#chatArrow1', { opacity: 1, duration: 0.2 }) .call(() => { document.getElementById('chatArrow1').classList.add('active'); }) // ===== 步骤2: AI 解析 ===== .to('#chatColAi', { opacity: 1, scale: 1, duration: 0.4, ease: 'power2.out' }) .call(() => { document.getElementById('chatColAi').classList.add('active'); }) // AI 任务依次完成 .call(() => setChatAiTaskState('chatAiTask1', 'processing'), null, '+=0.2') .call(() => setChatAiTaskState('chatAiTask1', 'completed'), null, '+=0.4') .call(() => setChatAiTaskState('chatAiTask2', 'processing'), null, '+=0.1') .call(() => setChatAiTaskState('chatAiTask2', 'completed'), null, '+=0.5') .call(() => setChatAiTaskState('chatAiTask3', 'processing'), null, '+=0.1') .call(() => setChatAiTaskState('chatAiTask3', 'completed'), null, '+=0.4') .call(() => setChatAiTaskState('chatAiTask4', 'processing'), null, '+=0.1') .call(() => setChatAiTaskState('chatAiTask4', 'completed'), null, '+=0.5') // 完成步骤2 .call(() => { document.getElementById('chatColAi').classList.remove('active'); document.getElementById('chatColAi').classList.add('completed'); }, null, '+=0.2') // ===== 箭头2 ===== .to('#chatArrow2', { opacity: 1, duration: 0.2 }) .call(() => { document.getElementById('chatArrow2').classList.add('active'); }) // ===== 步骤3: 智能截帧 ===== .to('#chatColFrames', { opacity: 1, scale: 1, duration: 0.4, ease: 'power2.out' }) .call(() => { document.getElementById('chatColFrames').classList.add('active'); }) // 逐个显示截帧 .call(() => showChatFrame(1), null, '+=0.2') .call(() => showChatFrame(2), null, '+=0.15') .call(() => showChatFrame(3), null, '+=0.15') .call(() => showChatFrame(4), null, '+=0.15') .call(() => showChatFrame(5), null, '+=0.15') .call(() => showChatFrame(6), null, '+=0.15') // 移除重复帧 .call(() => { const dupFrame = document.querySelector('.chat-frame-mini.dup'); if (dupFrame) dupFrame.classList.add('removing'); updateChatFrameStats(5, 1); }, null, '+=0.4') // 完成步骤3 .call(() => { document.getElementById('chatColFrames').classList.remove('active'); document.getElementById('chatColFrames').classList.add('completed'); }, null, '+=0.3') // ===== 箭头3 ===== .to('#chatArrow3', { opacity: 1, duration: 0.2 }) .call(() => { document.getElementById('chatArrow3').classList.add('active'); }) // ===== 步骤4: 导出 PDF ===== .to('#chatColExport', { opacity: 1, scale: 1, duration: 0.4, ease: 'power2.out' }) .call(() => { document.getElementById('chatColExport').classList.add('active'); }) // 显示导出文件 .call(() => { document.getElementById('chatExportFile').classList.add('show'); }, null, '+=0.3') // 完成步骤4 .call(() => { document.getElementById('chatColExport').classList.remove('active'); document.getElementById('chatColExport').classList.add('completed'); }, null, '+=0.5'); } function animateChatUploadProgress() { const progressBar = document.querySelector('#chatUploadProgress .chat-progress-bar'); if (!progressBar) return; let progress = 0; const interval = setInterval(() => { progress += Math.random() * 20 + 10; if (progress >= 100) { progress = 100; clearInterval(interval); } progressBar.style.setProperty('--progress', progress + '%'); const afterStyle = `linear-gradient(90deg, var(--primary) ${progress}%, var(--border) ${progress}%)`; progressBar.style.background = afterStyle; }, 80); } function setChatAiTaskState(id, state) { const el = document.getElementById(id); if (el) { el.classList.remove('processing', 'completed'); if (state) el.classList.add(state); } } function showChatFrame(frameNum) { const frame = document.querySelector(`.chat-frame-mini[data-frame="${frameNum}"]`); if (frame) frame.classList.add('show'); updateChatFrameStats(frameNum, frameNum === 5 ? 1 : 0); } function updateChatFrameStats(total, dup) { const countEl = document.getElementById('chatFrameCount'); const dupEl = document.getElementById('chatDupCount'); if (countEl) countEl.textContent = total; if (dupEl) dupEl.textContent = dup; } function resetChatFlowElements() { // 重置所有列 ['chatColUpload', 'chatColAi', 'chatColFrames', 'chatColExport'].forEach(id => { const el = document.getElementById(id); if (el) { el.classList.remove('active', 'completed'); gsap.set(el, { opacity: 0.4, scale: 0.95 }); } }); // 重置箭头 ['chatArrow1', 'chatArrow2', 'chatArrow3'].forEach(id => { const el = document.getElementById(id); if (el) { el.classList.remove('active'); gsap.set(el, { opacity: 0 }); } }); // 重置上传区域 const uploadBox = document.getElementById('chatUploadBox'); if (uploadBox) { uploadBox.style.display = 'block'; uploadBox.classList.remove('uploading'); const bar = uploadBox.querySelector('.chat-progress-bar'); if (bar) bar.style.background = ''; } // 重置视频完成状态 const videoDone = document.getElementById('chatVideoDone'); if (videoDone) videoDone.classList.remove('show'); // 重置 AI 任务 ['chatAiTask1', 'chatAiTask2', 'chatAiTask3', 'chatAiTask4'].forEach(id => { const el = document.getElementById(id); if (el) el.classList.remove('processing', 'completed'); }); // 重置截帧 document.querySelectorAll('.chat-frame-mini').forEach(frame => { frame.classList.remove('show', 'removing'); }); updateChatFrameStats(0, 0); // 重置导出文件 const exportFile = document.getElementById('chatExportFile'); if (exportFile) exportFile.classList.remove('show'); } function resetChatFlowAnimation() { if (chatFlowTimeline) chatFlowTimeline.kill(); chatFlowAnimationRunning = false; resetChatFlowElements(); const playBtn = document.getElementById('chatFlowPlayBtn'); if (playBtn) { playBtn.disabled = false; playBtn.innerHTML = '<span>▶</span> 播放演示'; } } // 聊天记录流程自动播放 document.addEventListener('DOMContentLoaded', () => { ScrollTrigger.create({ trigger: '#chatFlowDemo', start: 'top 70%', onEnter: () => { if (!chatFlowAnimationRunning) { setTimeout(startChatFlowAnimation, 500); } }, once: true }); }); // ========== 案件全流程管理与智能文书生成动画 V2 ========== let caseFlowAnimationRunning = false; let caseFlowTimeline = null; // 文件夹和文件节点 ID 列表(按新结构) const folderNodes = [ 'folder-trial', 'folder-filing', 'folder-1-1', 'folder-1-2', 'folder-1-3', 'folder-1-4', 'folder-1-5', 'folder-1-6', 'folder-1-7', 'folder-1-8', 'folder-hearing', 'folder-closing', 'folder-mail' ]; const fileNodes = [ 'file-1-1-1', 'file-1-2-1', 'file-1-3-1', 'file-1-3-2', 'file-1-3-3', 'file-1-4-1', 'file-1-4-2', 'file-1-5-1', 'file-1-6-1', 'file-1-7-1', 'file-1-7-2', 'file-2-1', 'file-2-2' ]; // 需要显示 NEW 标记的文件 const generatedFiles = [ 'file-1-1-1', 'file-1-2-1', 'file-1-3-1', 'file-1-3-2', 'file-1-3-3', 'file-1-4-1', 'file-1-4-2', 'file-1-5-1', 'file-1-6-1', 'file-1-7-1', 'file-1-7-2' ]; // 需要显示 NEW 标记的文件夹 const generatedFolders = ['folder-1-1', 'folder-1-2', 'folder-1-3', 'folder-1-4', 'folder-1-5', 'folder-1-6', 'folder-1-7']; function startCaseFlowAnimation() { if (caseFlowAnimationRunning) return; caseFlowAnimationRunning = true; const playBtn = document.getElementById('caseFlowPlayBtn'); playBtn.disabled = true; playBtn.innerHTML = '<span>⏸</span> 演示中...'; // 重置所有元素 resetCaseFlowElements(); // 创建 GSAP 时间线 caseFlowTimeline = gsap.timeline({ onComplete: () => { caseFlowAnimationRunning = false; playBtn.disabled = false; playBtn.innerHTML = '<span>▶</span> 重新播放'; } }); // ===== 步骤1: 显示上传区域 ===== caseFlowTimeline .to('#caseUploadBox', { opacity: 1, scale: 1, duration: 0.5, ease: 'power2.out' }) .call(() => { document.getElementById('caseUploadBox').classList.add('active'); }) // ===== 步骤2: 上传完成 ===== .to({}, { duration: 0.6 }) .call(() => { document.getElementById('caseUploadBox').classList.add('done'); }) // ===== 步骤3: 显示 AI 识别结果 ===== .to('#caseAiCompact', { opacity: 1, y: 0, duration: 0.4, ease: 'power2.out' }, '+=0.3') .call(() => { document.getElementById('caseAiCompact').classList.add('active'); }) // 逐个识别字段 .call(() => { const item = document.getElementById('caseAiItem1'); item.classList.add('active'); document.getElementById('caseAiType').textContent = '民间借贷纠纷'; }, null, '+=0.2') .call(() => { document.getElementById('caseAiItem1').classList.remove('active'); document.getElementById('caseAiItem1').classList.add('done'); document.getElementById('caseAiItem2').classList.add('active'); document.getElementById('caseAiPlaintiff').textContent = '张三'; }, null, '+=0.25') .call(() => { document.getElementById('caseAiItem2').classList.remove('active'); document.getElementById('caseAiItem2').classList.add('done'); document.getElementById('caseAiItem3').classList.add('active'); document.getElementById('caseAiDefendant').textContent = 'XX科技公司'; }, null, '+=0.25') .call(() => { document.getElementById('caseAiItem3').classList.remove('active'); document.getElementById('caseAiItem3').classList.add('done'); document.getElementById('caseAiItem4').classList.add('active'); document.getElementById('caseAiAmount').textContent = '¥50万'; }, null, '+=0.25') .call(() => { document.getElementById('caseAiItem4').classList.remove('active'); document.getElementById('caseAiItem4').classList.add('done'); }, null, '+=0.2') // ===== 步骤4: 显示生成状态 ===== .to('#caseGenStatus', { opacity: 1, y: 0, duration: 0.4, ease: 'power2.out' }, '+=0.3') .call(() => { document.getElementById('caseGenStatus').classList.add('active'); // 文件夹结构 - 处理中 const folderStatus = document.getElementById('caseGenFolder'); folderStatus.classList.add('processing'); folderStatus.querySelector('.status').textContent = '生成中...'; }) // ===== 步骤5: 显示文件夹树 ===== .to('#caseFolderTree', { opacity: 1, x: 0, duration: 0.5, ease: 'power2.out' }, '+=0.2') .call(() => { document.getElementById('caseFolderTree').classList.add('active'); }) // ===== 步骤6: 逐个显示文件夹节点 ===== // 一审 .call(() => showFolderNode('folder-trial'), null, '+=0.2') // 1-立案材料 .call(() => showFolderNode('folder-filing'), null, '+=0.15') // 1-起诉状文件夹及文件 .call(() => showFolderNode('folder-1-1'), null, '+=0.1') .call(() => showFileNode('file-1-1-1'), null, '+=0.08') // 2-当事人身份证明 .call(() => showFolderNode('folder-1-2'), null, '+=0.1') .call(() => showFileNode('file-1-2-1'), null, '+=0.08') // 3-委托代理人 .call(() => showFolderNode('folder-1-3'), null, '+=0.1') .call(() => showFileNode('file-1-3-1'), null, '+=0.08') .call(() => showFileNode('file-1-3-2'), null, '+=0.06') .call(() => showFileNode('file-1-3-3'), null, '+=0.06') // 4-证据材料 .call(() => showFolderNode('folder-1-4'), null, '+=0.1') .call(() => showFileNode('file-1-4-1'), null, '+=0.08') .call(() => showFileNode('file-1-4-2'), null, '+=0.06') // 5-送达地址确认书 .call(() => showFolderNode('folder-1-5'), null, '+=0.1') .call(() => showFileNode('file-1-5-1'), null, '+=0.08') // 6-退费账户确认书 .call(() => showFolderNode('folder-1-6'), null, '+=0.1') .call(() => showFileNode('file-1-6-1'), null, '+=0.08') // 7-保全申请书及保函 .call(() => showFolderNode('folder-1-7'), null, '+=0.1') .call(() => showFileNode('file-1-7-1'), null, '+=0.08') .call(() => showFileNode('file-1-7-2'), null, '+=0.06') // 8-其他立案材料 .call(() => showFolderNode('folder-1-8'), null, '+=0.08') // 2-庭审准备 .call(() => showFolderNode('folder-hearing'), null, '+=0.15') .call(() => showFileNode('file-2-1'), null, '+=0.08') .call(() => showFileNode('file-2-2'), null, '+=0.06') // 3-结案文书 .call(() => showFolderNode('folder-closing'), null, '+=0.15') // 4-邮件往来 .call(() => showFolderNode('folder-mail'), null, '+=0.15') // ===== 步骤7: 文件夹结构完成 ===== .call(() => { const folderStatus = document.getElementById('caseGenFolder'); folderStatus.classList.remove('processing'); folderStatus.classList.add('done'); folderStatus.querySelector('.status').textContent = '✓ 完成'; // 文书生成 - 处理中 const docsStatus = document.getElementById('caseGenDocs'); docsStatus.classList.add('processing'); docsStatus.querySelector('.status').textContent = '生成中...'; }, null, '+=0.2') // ===== 步骤8: 文书生成动画 - 显示 NEW 标记 ===== .call(() => markFileGenerated('file-1-1-1'), null, '+=0.2') .call(() => markFileGenerated('file-1-2-1'), null, '+=0.1') .call(() => markFileGenerated('file-1-3-1'), null, '+=0.1') .call(() => markFileGenerated('file-1-3-2'), null, '+=0.08') .call(() => markFileGenerated('file-1-3-3'), null, '+=0.08') .call(() => markFileGenerated('file-1-4-1'), null, '+=0.1') .call(() => markFileGenerated('file-1-4-2'), null, '+=0.08') .call(() => markFileGenerated('file-1-5-1'), null, '+=0.1') .call(() => markFileGenerated('file-1-6-1'), null, '+=0.1') .call(() => markFileGenerated('file-1-7-1'), null, '+=0.1') .call(() => markFileGenerated('file-1-7-2'), null, '+=0.08') // ===== 步骤9: 文书生成完成 ===== .call(() => { const docsStatus = document.getElementById('caseGenDocs'); docsStatus.classList.remove('processing'); docsStatus.classList.add('done'); docsStatus.querySelector('.status').textContent = '✓ 完成'; // 自动归档 - 处理中 const archiveStatus = document.getElementById('caseGenArchive'); archiveStatus.classList.add('processing'); archiveStatus.querySelector('.status').textContent = '归档中...'; }, null, '+=0.2') // ===== 步骤10: 归档完成 ===== .call(() => { const archiveStatus = document.getElementById('caseGenArchive'); archiveStatus.classList.remove('processing'); archiveStatus.classList.add('done'); archiveStatus.querySelector('.status').textContent = '✓ 完成'; // 更新顶部 badge const badge = document.getElementById('folderTreeBadge'); badge.textContent = '已完成'; badge.classList.add('done'); }, null, '+=0.5'); } function showFolderNode(id) { const el = document.getElementById(id); if (el) el.classList.add('show'); } function showFileNode(id) { const el = document.getElementById(id); if (el) el.classList.add('show'); } function markFileGenerated(id) { const el = document.getElementById(id); if (el) { el.classList.add('generated', 'highlight'); // 移除高亮效果 setTimeout(() => el.classList.remove('highlight'), 300); } } function resetCaseFlowElements() { // 重置上传区域 const uploadBox = document.getElementById('caseUploadBox'); if (uploadBox) { uploadBox.classList.remove('active', 'done'); gsap.set(uploadBox, { opacity: 0, scale: 0.9 }); } // 重置 AI 识别区 const aiCompact = document.getElementById('caseAiCompact'); if (aiCompact) { aiCompact.classList.remove('active'); gsap.set(aiCompact, { opacity: 0, y: 15 }); } // 重置识别项 ['caseAiItem1', 'caseAiItem2', 'caseAiItem3', 'caseAiItem4'].forEach(id => { const el = document.getElementById(id); if (el) el.classList.remove('active', 'done'); }); // 重置识别值 const resetValues = { 'caseAiType': '—', 'caseAiPlaintiff': '—', 'caseAiDefendant': '—', 'caseAiAmount': '—' }; Object.entries(resetValues).forEach(([id, val]) => { const el = document.getElementById(id); if (el) el.textContent = val; }); // 重置生成状态 const genStatus = document.getElementById('caseGenStatus'); if (genStatus) { genStatus.classList.remove('active'); gsap.set(genStatus, { opacity: 0, y: 15 }); } // 重置状态项 ['caseGenFolder', 'caseGenDocs', 'caseGenArchive'].forEach(id => { const el = document.getElementById(id); if (el) { el.classList.remove('processing', 'done'); const status = el.querySelector('.status'); if (status) status.textContent = '—'; } }); // 重置文件夹树 const folderTree = document.getElementById('caseFolderTree'); if (folderTree) { folderTree.classList.remove('active'); gsap.set(folderTree, { opacity: 0, x: 20 }); } // 重置 badge const badge = document.getElementById('folderTreeBadge'); if (badge) { badge.textContent = '生成中...'; badge.classList.remove('done'); } // 重置所有文件夹节点 folderNodes.forEach(id => { const el = document.getElementById(id); if (el) el.classList.remove('show'); }); // 重置所有文件节点 fileNodes.forEach(id => { const el = document.getElementById(id); if (el) el.classList.remove('show', 'generated', 'highlight'); }); } function resetCaseFlowAnimation() { if (caseFlowTimeline) caseFlowTimeline.kill(); caseFlowAnimationRunning = false; resetCaseFlowElements(); const playBtn = document.getElementById('caseFlowPlayBtn'); if (playBtn) { playBtn.disabled = false; playBtn.innerHTML = '<span>▶</span> 播放演示'; } } // 案件流程自动播放 document.addEventListener('DOMContentLoaded', () => { ScrollTrigger.create({ trigger: '#caseFlowDemo', start: 'top 70%', onEnter: () => { if (!caseFlowAnimationRunning) { setTimeout(startCaseFlowAnimation, 500); } }, once: true }); }); // ========== 模拟庭审动画 ========== let trialAnimationRunning = false; let trialTimeline = null; function startTrialAnimation() { if (trialAnimationRunning) return; trialAnimationRunning = true; const playBtn = document.getElementById('trialPlayBtn'); playBtn.disabled = true; playBtn.innerHTML = '<span>⏸</span> 演示中...'; resetTrialElements(); trialTimeline = gsap.timeline({ onComplete: () => { trialAnimationRunning = false; playBtn.disabled = false; playBtn.innerHTML = '<span>▶</span> 重新播放'; } }); const messages = ['trialMsg1', 'trialMsg2', 'trialMsg3', 'trialMsg4', 'trialMsg5', 'trialMsg6']; const chatEl = document.getElementById('courtChat'); // 法官席 + 角色图例出场 trialTimeline .call(() => { document.getElementById('judgeBench').classList.add('active'); }) .call(() => { document.getElementById('courtLegend').classList.add('active'); }, null, '+=0.3'); // 逐条显示对话气泡 messages.forEach((msgId, i) => { trialTimeline .call(() => { const el = document.getElementById(msgId); el.classList.add('show'); // 自动滚动到底部 chatEl.scrollTo({ top: chatEl.scrollHeight, behavior: 'smooth' }); }, null, '+=0.8'); }); } function resetTrialElements() { document.getElementById('judgeBench').classList.remove('active'); document.getElementById('courtLegend').classList.remove('active'); document.querySelectorAll('.chat-msg-row').forEach(b => b.classList.remove('show')); document.getElementById('courtChat').scrollTop = 0; } function resetTrialAnimation() { if (trialTimeline) trialTimeline.kill(); trialAnimationRunning = false; resetTrialElements(); const playBtn = document.getElementById('trialPlayBtn'); playBtn.disabled = false; playBtn.innerHTML = '<span>▶</span> 播放演示'; } // 模拟庭审自动播放 document.addEventListener('DOMContentLoaded', () => { ScrollTrigger.create({ trigger: '#courtroom', start: 'top 70%', onEnter: () => { if (!trialAnimationRunning) setTimeout(startTrialAnimation, 500); }, once: true }); }); // ========== 自动立案动画 ========== let filingAnimationRunning = false; let filingTimeline = null; const filingData = { oa: { stepsEl: 'filingStepsOa', formEl: 'filingFormOa', statusBar: 'filingStatusBarOa', statusText: 'filingStatusTextOa', steps: [ { icon: '🔐', label: '登录OA' }, { icon: '👤', label: '添加委托方' }, { icon: '📋', label: '案件信息' }, { icon: '🔍', label: '利冲检索' }, { icon: '📝', label: '合同信息' }, { icon: '💾', label: '存草稿' } ], forms: [ [{ label: '用户名', value: 'lawyer_zhang' }, { label: '密码', value: '••••••••' }], [{ label: '委托方', value: '张三' }, { label: '类型', value: '自然人' }, { label: '身份证号', value: '310***********1234' }], [{ label: '案由', value: '民间借贷纠纷' }, { label: '案件类型', value: '民商事' }, { label: '标的额', value: '500,000.00 元' }], [{ label: '利冲检索', value: '检索中...' }, { label: '检索结果', value: '✅ 未发现利益冲突' }], [{ label: '合同编号', value: 'HT-2026-0301' }, { label: '收费方式', value: '定额收费' }, { label: '律师费', value: '30,000.00 元' }], [{ label: '状态', value: '✅ 草稿已保存' }] ] }, court: { stepsEl: 'filingStepsCourt', formEl: 'filingFormCourt', statusBar: 'filingStatusBarCourt', statusText: 'filingStatusTextCourt', steps: [ { icon: '🔐', label: '登录系统' }, { icon: '🏛️', label: '选择法院' }, { icon: '📋', label: '填写信息' }, { icon: '📎', label: '上传材料' }, { icon: '✅', label: '确认提交' }, { icon: '📨', label: '提交成功' } ], forms: [ [{ label: '手机号', value: '138****8888' }, { label: '验证码', value: '自动识别中...' }], [{ label: '省份', value: '上海市' }, { label: '法院', value: '浦东新区人民法院' }, { label: '案件类型', value: '民事一审' }], [{ label: '原告', value: '张三' }, { label: '被告', value: 'XX科技有限公司' }, { label: '案由', value: '民间借贷纠纷' }], [{ label: '起诉状', value: '📄 起诉状.pdf' }, { label: '身份证明', value: '📄 身份证.pdf' }, { label: '证据材料', value: '📄 证据清单.pdf' }], [{ label: '材料核验', value: '✅ 全部通过' }, { label: '电子签名', value: '✅ 已签名' }], [{ label: '状态', value: '✅ 立案申请已提交,等待审核' }] ] } }; function initFilingSteps() { ['oa', 'court'].forEach(key => { const data = filingData[key]; const el = document.getElementById(data.stepsEl); if (!el) return; el.innerHTML = data.steps.map((s, i) => { let html = `<div class="filing-step" data-step="${i + 1}"> <div class="filing-step-circle">${s.icon}</div> <div class="filing-step-label">${s.label}</div> </div>`; if (i < data.steps.length - 1) html += '<div class="filing-step-line"></div>'; return html; }).join(''); }); } function animateChannel(timeline, data, offset) { data.steps.forEach((step, stepIdx) => { timeline.call(() => { const stepsEl = document.getElementById(data.stepsEl); const steps = stepsEl.querySelectorAll('.filing-step'); const lines = stepsEl.querySelectorAll('.filing-step-line'); steps.forEach((s, i) => { s.classList.remove('active', 'completed'); if (i < stepIdx) s.classList.add('completed'); else if (i === stepIdx) s.classList.add('active'); }); lines.forEach((l, i) => l.classList.toggle('filled', i < stepIdx)); const bar = document.getElementById(data.statusBar); bar.classList.add('show'); bar.classList.remove('success'); document.getElementById(data.statusText).textContent = `正在执行:${step.label}...`; const formEl = document.getElementById(data.formEl); formEl.innerHTML = ''; data.forms[stepIdx].forEach((field, fi) => { const row = document.createElement('div'); row.className = 'filing-form-row'; row.innerHTML = `<div class="filing-form-label">${field.label}</div><div class="filing-form-value">${field.value}</div>`; formEl.appendChild(row); setTimeout(() => { row.classList.add('show'); row.querySelector('.filing-form-value').classList.add('filled'); }, fi * 200 + 100); }); }, null, stepIdx === 0 ? `${offset}` : '+=1.2'); }); // 完成 timeline.call(() => { const stepsEl = document.getElementById(data.stepsEl); stepsEl.querySelectorAll('.filing-step').forEach(s => { s.classList.remove('active'); s.classList.add('completed'); }); stepsEl.querySelectorAll('.filing-step-line').forEach(l => l.classList.add('filled')); const bar = document.getElementById(data.statusBar); bar.classList.add('success'); document.getElementById(data.statusText).textContent = '🎉 立案流程已完成!'; }, null, '+=1.2'); } function startFilingAnimation() { if (filingAnimationRunning) return; filingAnimationRunning = true; const playBtn = document.getElementById('filingPlayBtn'); playBtn.disabled = true; playBtn.innerHTML = '<span>⏸</span> 演示中...'; resetFilingElements(); initFilingSteps(); // 两条时间线同时启动 const tlOa = gsap.timeline(); const tlCourt = gsap.timeline(); animateChannel(tlOa, filingData.oa, '+=0'); animateChannel(tlCourt, filingData.court, '+=0.3'); filingTimeline = gsap.timeline(); filingTimeline.add(tlOa, 0); filingTimeline.add(tlCourt, 0); filingTimeline.call(() => { filingAnimationRunning = false; playBtn.disabled = false; playBtn.innerHTML = '<span>▶</span> 重新播放'; }); } function resetFilingElements() { ['oa', 'court'].forEach(key => { const data = filingData[key]; const stepsEl = document.getElementById(data.stepsEl); if (stepsEl) { stepsEl.querySelectorAll('.filing-step').forEach(s => s.classList.remove('active', 'completed')); stepsEl.querySelectorAll('.filing-step-line').forEach(l => l.classList.remove('filled')); } const formEl = document.getElementById(data.formEl); if (formEl) formEl.innerHTML = ''; const bar = document.getElementById(data.statusBar); if (bar) bar.classList.remove('show', 'success'); }); } function resetFilingAnimation() { if (filingTimeline) filingTimeline.kill(); filingAnimationRunning = false; resetFilingElements(); initFilingSteps(); const playBtn = document.getElementById('filingPlayBtn'); playBtn.disabled = false; playBtn.innerHTML = '<span>▶</span> 播放演示'; } // 初始化步骤 & 自动播放 document.addEventListener('DOMContentLoaded', () => { initFilingSteps(); ScrollTrigger.create({ trigger: '#filingDemo', start: 'top 70%', onEnter: () => { if (!filingAnimationRunning) setTimeout(startFilingAnimation, 500); }, once: true }); }); // ========== 证据智能分类动画 ========== let evidenceAnimationRunning = false; let evidenceTimeline = null; const evidenceFiles = [ { id: 'evFile1', category: 'contract', icon: '📄', name: '借款合同' }, { id: 'evFile2', category: 'finance', icon: '🏦', name: '转账回单' }, { id: 'evFile3', category: 'identity', icon: '🪪', name: '身份证' }, { id: 'evFile4', category: 'chat', icon: '💬', name: '微信记录' }, { id: 'evFile5', category: 'finance', icon: '📊', name: '银行流水' }, { id: 'evFile6', category: 'contract', icon: '📑', name: '担保协议' }, { id: 'evFile7', category: 'photo', icon: '📸', name: '现场照片' }, { id: 'evFile8', category: 'identity', icon: '🏢', name: '营业执照' } ]; const categoryMeta = { contract: { name: '合同协议类', icon: '📄' }, finance: { name: '财务凭证类', icon: '🏦' }, identity: { name: '身份证明类', icon: '🪪' }, chat: { name: '通讯记录类', icon: '💬' }, photo: { name: '影像资料类', icon: '📸' } }; const categoryCounts = { contract: 0, finance: 0, identity: 0, chat: 0, photo: 0 }; function switchToCompareView() { const demo = document.getElementById('evidenceDemo'); demo.classList.add('compare-mode'); // 左侧:整理前 const sourceEl = document.getElementById('evidenceSource'); sourceEl.innerHTML = `<div class="compare-label compare-before-label">📂 整理前 · 散乱文件</div>` + `<div class="compare-file-list">` + evidenceFiles.map((f, i) => `<div class="compare-file-item"><span class="file-idx">${i + 1}.</span>${f.icon} ${f.name}</div>` ).join('') + `</div>` + `<div class="compare-stats">` + `<div class="compare-stat-row"><span class="stat-label">📎 文件总数</span><span class="stat-value">${evidenceFiles.length} 份</span></div>` + `<div class="compare-stat-row"><span class="stat-label">📂 分类数</span><span class="stat-value stat-bad">0 个(未分类)</span></div>` + `<div class="compare-stat-row"><span class="stat-label">🔍 可检索性</span><span class="stat-value stat-bad">❌ 无法快速定位</span></div>` + `<div class="compare-stat-row"><span class="stat-label">⏱ 人工整理</span><span class="stat-value stat-bad">~15 分钟</span></div>` + `</div>`; // 右侧:整理后 const grouped = {}; evidenceFiles.forEach(f => { if (!grouped[f.category]) grouped[f.category] = []; grouped[f.category].push(f); }); const resultEl = document.getElementById('evidenceResult'); resultEl.innerHTML = `<div class="compare-label compare-after-label">📁 整理后 · 自动归类</div>` + Object.entries(grouped).map(([cat, files]) => { const meta = categoryMeta[cat]; return `<div class="compare-folder"> <div class="compare-folder-header">${meta.icon} ${meta.name}(${files.length} 份)</div> <div class="compare-folder-files"> ${files.map(f => `<div class="compare-folder-file">${f.icon} ${f.name}</div>`).join('')} </div> </div>`; }).join('') + `<div class="compare-stats compare-stats-good">` + `<div class="compare-stat-row"><span class="stat-label">📎 文件总数</span><span class="stat-value">${evidenceFiles.length} 份</span></div>` + `<div class="compare-stat-row"><span class="stat-label">📂 分类数</span><span class="stat-value stat-good">${Object.keys(grouped).length} 个类别</span></div>` + `<div class="compare-stat-row"><span class="stat-label">🔍 可检索性</span><span class="stat-value stat-good">✅ 按类别秒级定位</span></div>` + `<div class="compare-stat-row"><span class="stat-label">⏱ AI 整理</span><span class="stat-value stat-good">~3 秒</span></div>` + `</div>`; // 逐个显示左边文件 setTimeout(() => { document.querySelectorAll('.compare-file-item').forEach((el, i) => { setTimeout(() => el.classList.add('show'), i * 60); }); }, 100); // 逐个展开右边文件夹 setTimeout(() => { document.querySelectorAll('.compare-folder').forEach((el, i) => { setTimeout(() => el.classList.add('show'), i * 150); }); }, 300); } function startEvidenceAnimation() { if (evidenceAnimationRunning) return; evidenceAnimationRunning = true; const playBtn = document.getElementById('evidencePlayBtn'); playBtn.disabled = true; playBtn.innerHTML = '<span>⏸</span> 演示中...'; resetEvidenceElements(); evidenceTimeline = gsap.timeline({ onComplete: () => { evidenceAnimationRunning = false; playBtn.disabled = false; playBtn.innerHTML = '<span>▶</span> 重新播放'; document.getElementById('aiProgressText').textContent = '✅ 分类完成'; document.getElementById('aiBrain').classList.remove('active'); } }); // 显示所有文件 evidenceTimeline.call(() => { document.querySelectorAll('.evidence-file').forEach((f, i) => { setTimeout(() => f.classList.add('show'), i * 100); }); }); // 显示分类目标 evidenceTimeline.call(() => { document.querySelectorAll('.evidence-category').forEach((c, i) => { setTimeout(() => c.classList.add('show'), i * 100); }); }, null, '+=1'); // 激活 AI evidenceTimeline.call(() => { document.getElementById('aiBrain').classList.add('active'); const txt = document.getElementById('aiProgressText'); txt.textContent = '分析中...'; txt.classList.add('active'); }, null, '+=0.5'); // 逐个分类文件 evidenceFiles.forEach((file, i) => { evidenceTimeline.call(() => { const el = document.getElementById(file.id); const catEl = document.getElementById('cat' + file.category.charAt(0).toUpperCase() + file.category.slice(1)); el.classList.add('flying'); catEl.classList.add('receiving'); setTimeout(() => catEl.classList.remove('receiving'), 500); categoryCounts[file.category]++; const countEl = document.getElementById('cat' + file.category.charAt(0).toUpperCase() + file.category.slice(1) + 'Count'); countEl.textContent = categoryCounts[file.category] + ' 份'; document.getElementById('aiProgressText').textContent = `分类中 ${i + 1}/${evidenceFiles.length}`; }, null, '+=0.5'); }); // 显示完成标记 evidenceTimeline.call(() => { Object.keys(categoryCounts).forEach(cat => { if (categoryCounts[cat] > 0) { const badge = document.getElementById('cat' + cat.charAt(0).toUpperCase() + cat.slice(1) + 'Badge'); badge.classList.add('show'); } }); }, null, '+=0.3'); // 切换为对比视图(在原区域内) evidenceTimeline.call(() => switchToCompareView(), null, '+=1.5'); } // 保存原始 HTML 用于重置 let evidenceSourceOriginal = ''; let evidenceResultOriginal = ''; document.addEventListener('DOMContentLoaded', () => { evidenceSourceOriginal = document.getElementById('evidenceSource').innerHTML; evidenceResultOriginal = document.getElementById('evidenceResult').innerHTML; }); function resetEvidenceElements() { Object.keys(categoryCounts).forEach(k => categoryCounts[k] = 0); const demo = document.getElementById('evidenceDemo'); demo.classList.remove('compare-mode'); // 恢复原始 HTML if (evidenceSourceOriginal) document.getElementById('evidenceSource').innerHTML = evidenceSourceOriginal; if (evidenceResultOriginal) document.getElementById('evidenceResult').innerHTML = evidenceResultOriginal; document.querySelectorAll('.evidence-file').forEach(f => f.classList.remove('show', 'flying')); document.querySelectorAll('.evidence-category').forEach(c => c.classList.remove('show', 'receiving')); document.querySelectorAll('.category-badge').forEach(b => b.classList.remove('show')); ['Contract', 'Finance', 'Identity', 'Chat', 'Photo'].forEach(cat => { const el = document.getElementById('cat' + cat + 'Count'); if (el) el.textContent = '0 份'; }); document.getElementById('aiBrain').classList.remove('active'); const txt = document.getElementById('aiProgressText'); txt.textContent = '等待开始'; txt.classList.remove('active'); } function resetEvidenceAnimation() { if (evidenceTimeline) evidenceTimeline.kill(); evidenceAnimationRunning = false; resetEvidenceElements(); const playBtn = document.getElementById('evidencePlayBtn'); playBtn.disabled = false; playBtn.innerHTML = '<span>▶</span> 播放演示'; } // 证据分类自动播放 document.addEventListener('DOMContentLoaded', () => { ScrollTrigger.create({ trigger: '#evidenceDemo', start: 'top 70%', onEnter: () => { if (!evidenceAnimationRunning) setTimeout(startEvidenceAnimation, 500); }, once: true }); }); </script>
<script>
// Three.js Particle Background
(function() {
const canvas = document.getElementById('bg-canvas');
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ canvas, alpha: true, antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
// Create particles
const particlesGeometry = new THREE.BufferGeometry();
const particlesCount = 2000;
const posArray = new Float32Array(particlesCount * 3);
for(let i = 0; i < particlesCount * 3; i++) {
posArray[i] = (Math.random() - 0.5) * 10;
}
particlesGeometry.setAttribute('position', new THREE.BufferAttribute(posArray, 3));
const particlesMaterial = new THREE.PointsMaterial({
size: 0.008,
color: 0x6366f1,
transparent: true,
opacity: 0.8,
blending: THREE.AdditiveBlending
});
const particlesMesh = new THREE.Points(particlesGeometry, particlesMaterial);
scene.add(particlesMesh);
camera.position.z = 3;
// Mouse interaction
let mouseX = 0, mouseY = 0;
document.addEventListener('mousemove', (e) => {
mouseX = (e.clientX / window.innerWidth) * 2 - 1;
mouseY = -(e.clientY / window.innerHeight) * 2 + 1;
});
// Animation
function animate() {
requestAnimationFrame(animate);
particlesMesh.rotation.x += 0.0003;
particlesMesh.rotation.y += 0.0005;
particlesMesh.rotation.x += mouseY * 0.0005;
particlesMesh.rotation.y += mouseX * 0.0005;
renderer.render(scene, camera);
}
animate();
// Resize handler
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
})();
// GSAP Animations
gsap.registerPlugin(ScrollTrigger);
// Nav scroll effect
window.addEventListener('scroll', () => {
const nav = document.getElementById('nav');
if (window.scrollY > 50) {
nav.classList.add('scrolled');
} else {
nav.classList.remove('scrolled');
}
});
// Reveal animations
const reveals = document.querySelectorAll('.reveal');
reveals.forEach((el, i) => {
gsap.fromTo(el,
{ opacity: 0, y: 40 },
{
opacity: 1,
y: 0,
duration: 0.8,
ease: 'power3.out',
scrollTrigger: {
trigger: el,
start: 'top 85%',
toggleActions: 'play none none none'
},
delay: i * 0.1
}
);
});
// 卡片入场动画 - 错落有致的 stagger 效果
const bentoCards = document.querySelectorAll('.bento-card');