Initial commit: 风影 OA 全栈源码(API/Web/Flutter/IM)

含 Phase 1.1 IM seq 排序、断线重连、多端已读同步与微信式语音转文字 UI。
排除 node_modules、构建产物、安装包与 .env 密钥。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-02 10:04:03 +00:00
commit 76f266645d
507 changed files with 99891 additions and 0 deletions
@@ -0,0 +1,67 @@
import { useEffect, useRef } from 'react';
export default function AmbientBackground() {
const canvasRef = useRef<HTMLCanvasElement | null>(null);
useEffect(() => {
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext('2d');
if (!ctx) return;
let frame = 0;
let width = (canvas.width = window.innerWidth);
let height = (canvas.height = window.innerHeight);
const onResize = () => {
width = canvas.width = window.innerWidth;
height = canvas.height = window.innerHeight;
};
window.addEventListener('resize', onResize);
const particles = Array.from({ length: 32 }, () => ({
x: Math.random() * width,
y: Math.random() * height,
size: Math.random() * 3 + 1.2,
speedY: -(Math.random() * 0.4 + 0.15),
speedX: (Math.random() - 0.5) * 0.2,
opacity: Math.random() * 0.5 + 0.2,
pulse: Math.random() * Math.PI,
pulseSpeed: Math.random() * 0.03 + 0.01,
}));
const render = () => {
ctx.clearRect(0, 0, width, height);
for (const p of particles) {
p.y += p.speedY;
p.x += p.speedX;
p.pulse += p.pulseSpeed;
if (p.y < -10) {
p.y = height + 10;
p.x = Math.random() * width;
}
if (p.x < -10) p.x = width + 10;
if (p.x > width + 10) p.x = -10;
const a = p.opacity * (0.7 + 0.3 * Math.sin(p.pulse));
ctx.beginPath();
ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
ctx.fillStyle = `rgba(107, 56, 212, ${a * 0.45})`;
ctx.fill();
}
frame = requestAnimationFrame(render);
};
render();
return () => {
window.removeEventListener('resize', onResize);
cancelAnimationFrame(frame);
};
}, []);
return (
<div className="yzl-ambient" aria-hidden>
<div className="yzl-orb yzl-orb-a" />
<div className="yzl-orb yzl-orb-b" />
<div className="yzl-orb yzl-orb-c" />
<canvas ref={canvasRef} className="yzl-particles" />
</div>
);
}