|
@@ -7,6 +7,7 @@ import CountUp from 'react-countup';
|
|
|
import { useInView } from 'react-intersection-observer';
|
|
import { useInView } from 'react-intersection-observer';
|
|
|
import { MapContainer, TileLayer, CircleMarker, Popup } from 'react-leaflet';
|
|
import { MapContainer, TileLayer, CircleMarker, Popup } from 'react-leaflet';
|
|
|
import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts';
|
|
import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts';
|
|
|
|
|
+import { Maximize2, Minimize2 } from 'lucide-react';
|
|
|
import 'leaflet/dist/leaflet.css';
|
|
import 'leaflet/dist/leaflet.css';
|
|
|
|
|
|
|
|
// 玻璃拟态卡片组件
|
|
// 玻璃拟态卡片组件
|
|
@@ -391,11 +392,69 @@ const ScanningLine = () => (
|
|
|
/>
|
|
/>
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
|
|
+// 全屏按钮组件
|
|
|
|
|
+const FullscreenToggle = () => {
|
|
|
|
|
+ const [isFullscreen, setIsFullscreen] = useState(false);
|
|
|
|
|
+
|
|
|
|
|
+ const toggleFullscreen = async () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ if (!document.fullscreenElement) {
|
|
|
|
|
+ await document.documentElement.requestFullscreen();
|
|
|
|
|
+ setIsFullscreen(true);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ await document.exitFullscreen();
|
|
|
|
|
+ setIsFullscreen(false);
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('Error toggling fullscreen:', error);
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ const handleFullscreenChange = () => {
|
|
|
|
|
+ setIsFullscreen(!!document.fullscreenElement);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ document.addEventListener('fullscreenchange', handleFullscreenChange);
|
|
|
|
|
+
|
|
|
|
|
+ // 添加F11快捷键支持
|
|
|
|
|
+ const handleKeyPress = (event: KeyboardEvent) => {
|
|
|
|
|
+ if (event.key === 'F11') {
|
|
|
|
|
+ event.preventDefault();
|
|
|
|
|
+ toggleFullscreen();
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ document.addEventListener('keydown', handleKeyPress);
|
|
|
|
|
+
|
|
|
|
|
+ return () => {
|
|
|
|
|
+ document.removeEventListener('fullscreenchange', handleFullscreenChange);
|
|
|
|
|
+ document.removeEventListener('keydown', handleKeyPress);
|
|
|
|
|
+ };
|
|
|
|
|
+ }, []);
|
|
|
|
|
+
|
|
|
|
|
+ return (
|
|
|
|
|
+ <motion.button
|
|
|
|
|
+ onClick={toggleFullscreen}
|
|
|
|
|
+ className="bg-white/10 backdrop-blur-md border border-white/20 rounded-lg p-2 hover:bg-white/20 transition-all duration-300"
|
|
|
|
|
+ whileHover={{ scale: 1.1 }}
|
|
|
|
|
+ whileTap={{ scale: 0.9 }}
|
|
|
|
|
+ title={isFullscreen ? '退出全屏' : '进入全屏'}
|
|
|
|
|
+ >
|
|
|
|
|
+ {isFullscreen ? (
|
|
|
|
|
+ <Minimize2 className="w-5 h-5 text-cyan-400" />
|
|
|
|
|
+ ) : (
|
|
|
|
|
+ <Maximize2 className="w-5 h-5 text-cyan-400" />
|
|
|
|
|
+ )}
|
|
|
|
|
+ </motion.button>
|
|
|
|
|
+ );
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
// 主仪表盘组件
|
|
// 主仪表盘组件
|
|
|
const DashboardGrid = () => (
|
|
const DashboardGrid = () => (
|
|
|
<div className="flex-1 p-4 md:p-8 grid grid-cols-12 gap-4">
|
|
<div className="flex-1 p-4 md:p-8 grid grid-cols-12 gap-4">
|
|
|
{/* 顶部标题区 */}
|
|
{/* 顶部标题区 */}
|
|
|
- <motion.div
|
|
|
|
|
|
|
+ <motion.div
|
|
|
className="col-span-12 h-20"
|
|
className="col-span-12 h-20"
|
|
|
initial={{ opacity: 0, y: -50 }}
|
|
initial={{ opacity: 0, y: -50 }}
|
|
|
animate={{ opacity: 1, y: 0 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
@@ -405,7 +464,10 @@ const DashboardGrid = () => (
|
|
|
<h1 className="text-2xl md:text-4xl font-bold bg-gradient-to-r from-cyan-400 to-purple-400 bg-clip-text text-transparent">
|
|
<h1 className="text-2xl md:text-4xl font-bold bg-gradient-to-r from-cyan-400 to-purple-400 bg-clip-text text-transparent">
|
|
|
银龄智慧数据大屏
|
|
银龄智慧数据大屏
|
|
|
</h1>
|
|
</h1>
|
|
|
- <DigitalClock />
|
|
|
|
|
|
|
+ <div className="flex items-center space-x-4">
|
|
|
|
|
+ <DigitalClock />
|
|
|
|
|
+ <FullscreenToggle />
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
</motion.div>
|
|
</motion.div>
|
|
|
|
|
|