|
|
@@ -0,0 +1,265 @@
|
|
|
+import { useState, useEffect } from 'react';
|
|
|
+
|
|
|
+interface VariationModalProps {
|
|
|
+ isOpen: boolean;
|
|
|
+ onClose: () => void;
|
|
|
+}
|
|
|
+
|
|
|
+// 变化幅度静态数据
|
|
|
+const variationData = [
|
|
|
+ { year: '2021年', assetTotal: 21.36, assetNet: 3.78, debtRatio: -2.63, income: 50.82, profitTotal: -143.75, profitNet: -141.18 },
|
|
|
+ { year: '2022年', assetTotal: 21.36, assetNet: 3.78, debtRatio: 3.00, income: 50.82, profitTotal: 153.33, profitNet: 134.71 },
|
|
|
+ { year: '2023年', assetTotal: 109.29, assetNet: 330.14, debtRatio: -24.95, income: 109.29, profitTotal: 60.00, profitNet: 1.69 },
|
|
|
+ { year: '2024年', assetTotal: 51.76, assetNet: 64.86, debtRatio: -4.18, income: 51.76, profitTotal: 28.91, profitNet: 103.33 },
|
|
|
+ { year: '2025年', assetTotal: 8.73, assetNet: 3.41, debtRatio: 2.58, income: 8.73, profitTotal: -18.79, profitNet: -18.03 }
|
|
|
+];
|
|
|
+
|
|
|
+// 颜色配置 - 与主页面四个图的柱子颜色保持一致
|
|
|
+// 颜色值直接在组件中使用,确保与主页面四个图的柱子颜色保持一致
|
|
|
+
|
|
|
+export default function VariationModal({ isOpen, onClose }: VariationModalProps) {
|
|
|
+ const [isVisible, setIsVisible] = useState(false);
|
|
|
+
|
|
|
+ useEffect(() => {
|
|
|
+ if (isOpen) {
|
|
|
+ setIsVisible(true);
|
|
|
+ } else {
|
|
|
+ const timer = setTimeout(() => setIsVisible(false), 300);
|
|
|
+ return () => clearTimeout(timer);
|
|
|
+ }
|
|
|
+ }, [isOpen]);
|
|
|
+
|
|
|
+ // ESC键关闭弹窗
|
|
|
+ useEffect(() => {
|
|
|
+ const handleEsc = (event: KeyboardEvent) => {
|
|
|
+ if (event.key === 'Escape') {
|
|
|
+ onClose();
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ if (isOpen) {
|
|
|
+ document.addEventListener('keydown', handleEsc);
|
|
|
+ return () => document.removeEventListener('keydown', handleEsc);
|
|
|
+ }
|
|
|
+ }, [isOpen, onClose]);
|
|
|
+
|
|
|
+ if (!isVisible) return null;
|
|
|
+
|
|
|
+ return (
|
|
|
+ <div className={`fixed inset-0 z-50 flex items-center justify-center transition-opacity duration-300 ${isOpen ? 'opacity-100' : 'opacity-0'}`}>
|
|
|
+ {/* 遮罩层 */}
|
|
|
+ <div
|
|
|
+ className="absolute inset-0 backdrop-blur-[5px] backdrop-filter bg-[rgba(0,0,0,0.75)]"
|
|
|
+ onClick={onClose}
|
|
|
+ />
|
|
|
+
|
|
|
+ {/* 弹窗容器 */}
|
|
|
+ <div className="relative h-[852px] w-[1440px] overflow-hidden">
|
|
|
+ {/* 弹窗背景 */}
|
|
|
+ <div aria-hidden="true" className="absolute inset-0 pointer-events-none">
|
|
|
+ <img
|
|
|
+ alt=""
|
|
|
+ className="absolute max-w-none object-50%-50% object-cover size-full"
|
|
|
+ src="/financial-dashboard/background-property1.png"
|
|
|
+ />
|
|
|
+ <div className="absolute bg-[rgba(33,33,33,0.6)] inset-0" />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 弹窗内容 */}
|
|
|
+ <div className="absolute content-stretch flex flex-col gap-[20px] items-center left-1/2 top-1/2 translate-x-[-50%] translate-y-[-50%] w-[1274px]">
|
|
|
+ {/* 标题 */}
|
|
|
+ <div className="flex flex-col font-['HarmonyOS_Sans_SC:Regular',sans-serif] h-[60px] justify-center leading-[0] not-italic relative shrink-0 text-[28px] text-center text-white w-full">
|
|
|
+ <p className="leading-[normal] whitespace-pre-wrap">变动幅度</p>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 图例区域 */}
|
|
|
+ <div className="content-stretch flex flex-col gap-[20px] items-start relative shrink-0 w-full">
|
|
|
+ <div className="content-stretch flex flex-col gap-[10px] h-[94px] items-start relative shrink-0 w-full">
|
|
|
+ <div className="content-stretch flex h-[58px] items-center justify-between relative shrink-0 w-full">
|
|
|
+ <div className="content-stretch flex gap-[20px] items-center relative shrink-0">
|
|
|
+ <div className="content-stretch flex gap-[20px] items-center relative shrink-0">
|
|
|
+ {/* 资产总额图例 */}
|
|
|
+ <div className="content-stretch flex gap-[10px] h-[26px] items-center relative shrink-0">
|
|
|
+ <div className="flex items-center justify-center relative shrink-0">
|
|
|
+ <div className="flex-none rotate-[180deg]">
|
|
|
+ <div className="box-border content-stretch flex flex-col items-center pb-[3px] pt-0 px-0 relative size-[14px]">
|
|
|
+ <div className="bg-gradient-to-b from-[#1e40af] to-[#3b82f6] h-full w-full rounded-sm" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div className="flex flex-col font-['PingFang_SC:Regular',sans-serif] justify-center leading-[0] not-italic relative shrink-0 text-[18px] text-white w-[83px]">
|
|
|
+ <p className="leading-[25.2px] whitespace-pre-wrap">资产总额</p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 资产净额图例 */}
|
|
|
+ <div className="content-stretch flex gap-[20px] h-[26px] items-center relative shrink-0">
|
|
|
+ <div className="flex items-center justify-center relative shrink-0">
|
|
|
+ <div className="flex-none rotate-[180deg]">
|
|
|
+ <div className="box-border content-stretch flex flex-col items-center pb-[3px] pt-0 px-0 relative size-[14px]">
|
|
|
+ <div className="bg-gradient-to-b from-[#f59e0b] to-[#fbbf24] h-full w-full rounded-sm" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div className="flex flex-col font-['PingFang_SC:Regular',sans-serif] h-[26px] justify-center leading-[0] not-italic relative shrink-0 text-[18px] text-white w-[80px]">
|
|
|
+ <p className="leading-[25.2px] whitespace-pre-wrap">资产净额</p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 资产负债率图例 */}
|
|
|
+ <div className="content-stretch flex gap-[20px] h-[26px] items-center relative shrink-0">
|
|
|
+ <div className="flex items-center justify-center relative shrink-0">
|
|
|
+ <div className="flex-none rotate-[180deg]">
|
|
|
+ <div className="box-border content-stretch flex flex-col items-center pb-[3px] pt-0 px-0 relative size-[14px]">
|
|
|
+ <div className="bg-gradient-to-b from-[#3b82f6] to-[#60a5fa] h-full w-full rounded-sm" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div className="flex flex-col font-['PingFang_SC:Regular',sans-serif] justify-center leading-[0] not-italic relative shrink-0 text-[18px] text-white whitespace-nowrap">
|
|
|
+ <p className="leading-[25.2px]">资产负债率</p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 收入图例 */}
|
|
|
+ <div className="content-stretch flex gap-[20px] h-[26px] items-center relative shrink-0">
|
|
|
+ <div className="flex items-center justify-center relative shrink-0">
|
|
|
+ <div className="flex-none rotate-[180deg]">
|
|
|
+ <div className="box-border content-stretch flex flex-col items-center pb-[3px] pt-0 px-0 relative size-[14px]">
|
|
|
+ <div className="bg-gradient-to-b from-[#10b981] to-[#34d399] h-full w-full rounded-sm" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div className="flex flex-col font-['PingFang_SC:Regular',sans-serif] justify-center leading-[0] not-italic relative shrink-0 text-[18px] text-white whitespace-nowrap">
|
|
|
+ <p className="leading-[25.2px]">收入</p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 利润总额图例 */}
|
|
|
+ <div className="content-stretch flex gap-[20px] h-[26px] items-center relative shrink-0">
|
|
|
+ <div className="flex items-center justify-center relative shrink-0">
|
|
|
+ <div className="flex-none rotate-[180deg]">
|
|
|
+ <div className="box-border content-stretch flex flex-col items-center pb-[3px] pt-0 px-0 relative size-[14px]">
|
|
|
+ <div className="bg-gradient-to-b from-[#f59e0b] to-[#fbbf24] h-full w-full rounded-sm" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div className="flex flex-col font-['PingFang_SC:Regular',sans-serif] justify-center leading-[0] not-italic relative shrink-0 text-[18px] text-white whitespace-nowrap">
|
|
|
+ <p className="leading-[25.2px]">利润总额</p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 净利润图例 */}
|
|
|
+ <div className="content-stretch flex gap-[20px] h-[26px] items-center relative shrink-0">
|
|
|
+ <div className="flex items-center justify-center relative shrink-0">
|
|
|
+ <div className="flex-none rotate-[180deg]">
|
|
|
+ <div className="box-border content-stretch flex flex-col items-center pb-[3px] pt-0 px-0 relative size-[14px]">
|
|
|
+ <div className="bg-gradient-to-b from-[#8b5cf6] to-[#a78bfa] h-full w-full rounded-sm" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div className="flex flex-col font-['PingFang_SC:Regular',sans-serif] justify-center leading-[0] not-italic relative shrink-0 text-[18px] text-white whitespace-nowrap">
|
|
|
+ <p className="leading-[25.2px]">净利润</p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <p className="font-['PingFang_SC:Regular',sans-serif] leading-[25.2px] not-italic relative shrink-0 text-[14px] text-[rgba(255,255,255,0.8)] w-[156px] whitespace-pre-wrap">
|
|
|
+ *数据截止至2025年9月
|
|
|
+ </p>
|
|
|
+ </div>
|
|
|
+ <p className="font-['PingFang_SC:Regular',sans-serif] leading-[25.2px] not-italic relative shrink-0 text-[14px] text-[rgba(255,255,255,0.8)] w-[74.639px] whitespace-pre-wrap">
|
|
|
+ 单元:%
|
|
|
+ </p>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 图表区域 */}
|
|
|
+ <div className="grid-cols-[max-content] grid-rows-[max-content] inline-grid justify-items-start leading-[0] relative shrink-0">
|
|
|
+ <div className="box-border col-[1] content-stretch flex items-center ml-0 mt-0 relative row-[1] w-[1274px]">
|
|
|
+ {/* Y轴刻度 */}
|
|
|
+ <div className="box-border content-stretch flex flex-col font-['PingFang_SC:Regular',sans-serif] h-[475px] items-end justify-between leading-[20px] not-italic px-[10px] py-0 relative shrink-0 text-[15px] text-right text-white w-[82px]">
|
|
|
+ <p className="relative shrink-0">400</p>
|
|
|
+ <p className="relative shrink-0">350</p>
|
|
|
+ <p className="relative shrink-0">300</p>
|
|
|
+ <p className="relative shrink-0">250</p>
|
|
|
+ <p className="relative shrink-0">200</p>
|
|
|
+ <p className="relative shrink-0">150</p>
|
|
|
+ <p className="relative shrink-0">100</p>
|
|
|
+ <p className="relative shrink-0">50</p>
|
|
|
+ <p className="relative shrink-0">0</p>
|
|
|
+ <p className="relative shrink-0">-50</p>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 图表区域 */}
|
|
|
+ <div className="box-border content-stretch flex flex-col h-[511px] items-start justify-between px-0 py-[20px] relative shrink-0 w-[1197px]">
|
|
|
+ {/* 网格线 */}
|
|
|
+ {[400, 350, 300, 250, 200, 150, 100, 50, 0, -50].map((_, index) => (
|
|
|
+ <div key={index} className="flex items-center justify-center relative shrink-0">
|
|
|
+ <div className="flex-none scale-y-[-100%]">
|
|
|
+ <div className="content-stretch flex flex-col gap-[10px] h-[20px] items-start justify-center relative">
|
|
|
+ <div className="h-[1.771px] relative shrink-0 w-[1197.042px]">
|
|
|
+ <div className="bg-[rgba(255,255,255,0.1)] h-full w-full" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ ))}
|
|
|
+
|
|
|
+ {/* 柱状图数据展示 */}
|
|
|
+ <div className="absolute bottom-0 left-[82px] right-0 top-0 flex items-end justify-between px-[40px]">
|
|
|
+ {variationData.map((data, index) => (
|
|
|
+ <div key={index} className="flex flex-col items-center gap-2">
|
|
|
+ {/* 年份标签 */}
|
|
|
+ <p className="font-['PingFang_SC:Regular',sans-serif] text-[14px] text-white">
|
|
|
+ {data.year}
|
|
|
+ </p>
|
|
|
+
|
|
|
+ {/* 柱子组 */}
|
|
|
+ <div className="flex gap-1 items-end h-[400px]">
|
|
|
+ {/* 资产总额 */}
|
|
|
+ <div
|
|
|
+ className="w-4 bg-gradient-to-b from-[#1e40af] to-[#3b82f6] rounded-t-sm"
|
|
|
+ style={{ height: `${Math.max(0, data.assetTotal + 50) * 0.9}px` }}
|
|
|
+ />
|
|
|
+
|
|
|
+ {/* 资产净额 */}
|
|
|
+ <div
|
|
|
+ className="w-4 bg-gradient-to-b from-[#f59e0b] to-[#fbbf24] rounded-t-sm"
|
|
|
+ style={{ height: `${Math.max(0, data.assetNet + 50) * 0.9}px` }}
|
|
|
+ />
|
|
|
+
|
|
|
+ {/* 资产负债率 */}
|
|
|
+ <div
|
|
|
+ className="w-4 bg-gradient-to-b from-[#3b82f6] to-[#60a5fa] rounded-t-sm"
|
|
|
+ style={{ height: `${Math.max(0, data.debtRatio + 50) * 0.9}px` }}
|
|
|
+ />
|
|
|
+
|
|
|
+ {/* 收入 */}
|
|
|
+ <div
|
|
|
+ className="w-4 bg-gradient-to-b from-[#10b981] to-[#34d399] rounded-t-sm"
|
|
|
+ style={{ height: `${Math.max(0, data.income + 50) * 0.9}px` }}
|
|
|
+ />
|
|
|
+
|
|
|
+ {/* 利润总额 */}
|
|
|
+ <div
|
|
|
+ className="w-4 bg-gradient-to-b from-[#f59e0b] to-[#fbbf24] rounded-t-sm"
|
|
|
+ style={{ height: `${Math.max(0, data.profitTotal + 50) * 0.9}px` }}
|
|
|
+ />
|
|
|
+
|
|
|
+ {/* 净利润 */}
|
|
|
+ <div
|
|
|
+ className="w-4 bg-gradient-to-b from-[#8b5cf6] to-[#a78bfa] rounded-t-sm"
|
|
|
+ style={{ height: `${Math.max(0, data.profitNet + 50) * 0.9}px` }}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ ))}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+}
|