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>