Bladeren bron

📝 docs(stories): add chart implementation decision documentation

- document chart implementation decision for financial data modules
- specify unified use of Recharts library for all financial charts
- require legend colors to match bar colors according to figma-jsx.md规范
- ensure color styles match Figma design as closely as possible

✨ feat(AssetMetrics): implement bar chart using Recharts

- replace custom bar chart implementation with Recharts library
- add responsive container for better layout adaptation
- implement gradient colors for chart bars
- add tooltip with formatted values
- maintain original visual design while improving code maintainability
- remove hardcoded bar heights and position calculations
yourname 2 maanden geleden
bovenliggende
commit
c7bbadfa7f

+ 7 - 0
docs/stories/006.003.实现财务数据可视化大屏静态页面.md

@@ -226,6 +226,13 @@ for file in *.png; do if file "$file" | grep -q "SVG"; then mv "$file" "${file%.
 - 将所有Tailwind类转换为目标样式系统,同时保持精确的视觉设计
 - 将所有Tailwind类转换为目标样式系统,同时保持精确的视觉设计
 - 遵循项目的现有模式和约定
 - 遵循项目的现有模式和约定
 
 
+### 图表实现决策
+**统一使用Recharts图表库**:
+- 所有财务数据模块统一使用Recharts实现图表
+- 图例颜色与柱子颜色保持一致,按照figma-jsx.md设计规范
+- 保持与现有项目技术栈的一致性
+- 确保颜色样式尽量匹配Figma设计
+
 ### 编码标准 [Source: architecture/coding-standards.md#关键集成规则]
 ### 编码标准 [Source: architecture/coding-standards.md#关键集成规则]
 - **现有API兼容性**: 确保组件不破坏现有API契约
 - **现有API兼容性**: 确保组件不破坏现有API契约
 - **错误处理**: 实现适当的错误边界和加载状态
 - **错误处理**: 实现适当的错误边界和加载状态

+ 66 - 78
src/client/home/pages/FinancialDashboard/components/AssetMetrics.tsx

@@ -2,6 +2,7 @@ import BarElement from './BarElement';
 import BaseContainer from './BaseContainer';
 import BaseContainer from './BaseContainer';
 import ModuleHeader from './ModuleHeader';
 import ModuleHeader from './ModuleHeader';
 import ModuleHeaderBackground from './ModuleHeaderBackground';
 import ModuleHeaderBackground from './ModuleHeaderBackground';
+import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts';
 
 
 interface AssetMetricsProps {
 interface AssetMetricsProps {
   className?: string;
   className?: string;
@@ -15,14 +16,12 @@ const assetData = [
   { year: '2025年', assetTotal: 840.12, assetNet: 421.55 }
   { year: '2025年', assetTotal: 840.12, assetNet: 421.55 }
 ];
 ];
 
 
-// 按照Figma设计规范中的精确柱形高度
-const barHeights = {
-  '2021年': { assetTotal: 90, assetNet: 41 },
-  '2022年': { assetTotal: 100, assetNet: 48 },
-  '2023年': { assetTotal: 145, assetNet: 92 },
-  '2024年': { assetTotal: 175, assetNet: 122 },
-  '2025年': { assetTotal: 180, assetNet: 125 }
-};
+// Recharts格式的数据
+const chartData = assetData.map(item => ({
+  year: item.year,
+  '资产总额': item.assetTotal,
+  '资产净额': item.assetNet
+}));
 
 
 export function AssetMetrics({ className }: AssetMetricsProps) {
 export function AssetMetrics({ className }: AssetMetricsProps) {
   return (
   return (
@@ -30,7 +29,7 @@ export function AssetMetrics({ className }: AssetMetricsProps) {
       {/* 底部容器 - 使用BaseContainer作为背景 */}
       {/* 底部容器 - 使用BaseContainer作为背景 */}
       <BaseContainer className="absolute h-[490px] left-0 overflow-clip top-0 w-[930px]" />
       <BaseContainer className="absolute h-[490px] left-0 overflow-clip top-0 w-[930px]" />
 
 
-      {/* 表格区域 */}
+      {/* 表格区域 - 使用Recharts实现 */}
       <div className="absolute content-stretch flex flex-col gap-[20px] h-[388px] items-start left-[41.4px] top-[79px] w-[847.191px]">
       <div className="absolute content-stretch flex flex-col gap-[20px] h-[388px] items-start left-[41.4px] top-[79px] w-[847.191px]">
           {/* 顶部标题和说明 */}
           {/* 顶部标题和说明 */}
           <div className="content-stretch flex items-start justify-between relative shrink-0 w-[847px]">
           <div className="content-stretch flex items-start justify-between relative shrink-0 w-[847px]">
@@ -65,75 +64,64 @@ export function AssetMetrics({ className }: AssetMetricsProps) {
             单位:亿元
             单位:亿元
           </p>
           </p>
 
 
-          {/* 年份标签 */}
-          <div className="grid-cols-[max-content] grid-rows-[max-content] inline-grid justify-items-start leading-[0] relative shrink-0">
-            <div className="col-[1] grid-cols-[max-content] grid-rows-[max-content] inline-grid justify-items-start leading-[21.504px] ml-[93px] mt-[calc(50%+125.72px)] relative row-[1] text-[15px] text-[rgba(255,255,255,0.8)] whitespace-pre-wrap">
-              {assetData.map((data, index) => (
-                <p
-                  key={data.year}
-                  className={`col-[1] mt-0 relative row-[1] ${
-                    index === 0 ? 'ml-0 w-[49.92px]' :
-                    index === 1 ? 'ml-[207.76px] text-right translate-x-[-100%] w-[53.76px]' :
-                    index === 2 ? 'ml-[376.68px] text-right translate-x-[-100%] w-[55.68px]' :
-                    index === 3 ? 'ml-[490px] w-[52.48px]' :
-                    'ml-[656px] mt-px w-[58px]'
-                  }`}
-                >
-                  {data.year}
-                </p>
-              ))}
-            </div>
-
-            {/* 网格线背景 */}
-            <div className="col-[1] grid-cols-[max-content] grid-rows-[max-content] inline-grid justify-items-start ml-[52px] mt-0 relative row-[1]">
-              {/* 水平网格线 */}
-              <div className="col-[1] h-[2px] ml-[0.12px] mt-0 relative row-[1] w-[794.879px] bg-gradient-to-r from-[rgba(255,255,255,0.1)] to-[rgba(255,255,255,0.05)]" />
-              <div className="col-[1] h-[2px] ml-0 mt-[40px] relative row-[1] w-[795px] bg-gradient-to-r from-[rgba(255,255,255,0.1)] to-[rgba(255,255,255,0.05)]" />
-              <div className="col-[1] h-[2px] ml-0 mt-[80px] relative row-[1] w-[795px] bg-gradient-to-r from-[rgba(255,255,255,0.1)] to-[rgba(255,255,255,0.05)]" />
-              <div className="col-[1] h-[2px] ml-0 mt-[120px] relative row-[1] w-[795px] bg-gradient-to-r from-[rgba(255,255,255,0.1)] to-[rgba(255,255,255,0.05)]" />
-              <div className="col-[1] h-[2px] ml-0 mt-[160px] relative row-[1] w-[795px] bg-gradient-to-r from-[rgba(255,255,255,0.1)] to-[rgba(255,255,255,0.05)]" />
-              <div className="col-[1] h-[2px] ml-0 mt-[200px] relative row-[1] w-[795.191px] bg-gradient-to-r from-[rgba(255,255,255,0.1)] to-[rgba(255,255,255,0.05)]" />
-              <div className="col-[1] h-[2px] ml-0 mt-[240px] relative row-[1] w-[795px] bg-gradient-to-r from-[rgba(255,255,255,0.1)] to-[rgba(255,255,255,0.05)]" />
-            </div>
-          </div>
-
-          {/* 柱形图数据 */}
-          <div className="box-border col-[1] content-stretch flex items-end justify-between ml-[69px] mt-[3.44px] relative row-[1] w-[753px]">
-            {assetData.map((data) => (
-              <div key={data.year} className="content-stretch flex gap-[10px] items-end relative shrink-0">
-                {/* 资产总额 */}
-                <div className="content-stretch flex flex-col gap-[10px] items-center relative shrink-0 w-[43px]">
-                  <p className="leading-[21.504px] min-w-full not-italic relative shrink-0 text-[13px] text-white w-[min-content] whitespace-pre-wrap">
-                    {data.assetTotal}
-                  </p>
-                  <div className="flex items-center justify-center relative shrink-0">
-                    <div className="flex-none rotate-[180deg]">
-                      <BarElement
-                        variant="asset-total"
-                        className={`w-[35px]`}
-                        style={{ height: `${barHeights[data.year as keyof typeof barHeights].assetTotal}px` }}
-                      />
-                    </div>
-                  </div>
-                </div>
-
-                {/* 资产净额 */}
-                <div className="content-stretch flex flex-col gap-[10px] items-center relative shrink-0 w-[35px]">
-                  <p className="leading-[21.504px] min-w-full not-italic relative shrink-0 text-[13px] text-white w-[min-content] whitespace-pre-wrap">
-                    {data.assetNet}
-                  </p>
-                  <div className="flex items-center justify-center relative shrink-0">
-                    <div className="flex-none rotate-[180deg]">
-                      <BarElement
-                        variant="asset-net"
-                        className={`w-[35px] rounded-[2px]`}
-                        style={{ height: `${barHeights[data.year as keyof typeof barHeights].assetNet}px` }}
-                      />
-                    </div>
-                  </div>
-                </div>
-              </div>
-            ))}
+          {/* Recharts图表区域 */}
+          <div className="w-full h-[300px] mt-4">
+            <ResponsiveContainer width="100%" height="100%">
+              <BarChart
+                data={chartData}
+                margin={{ top: 20, right: 30, left: 20, bottom: 5 }}
+              >
+                <CartesianGrid
+                  strokeDasharray="3 3"
+                  stroke="rgba(255,255,255,0.1)"
+                  horizontal={true}
+                  vertical={false}
+                />
+                <XAxis
+                  dataKey="year"
+                  axisLine={false}
+                  tickLine={false}
+                  tick={{ fill: 'rgba(255,255,255,0.8)', fontSize: 14 }}
+                />
+                <YAxis
+                  axisLine={false}
+                  tickLine={false}
+                  tick={{ fill: 'rgba(255,255,255,0.8)', fontSize: 12 }}
+                />
+                <Tooltip
+                  contentStyle={{
+                    backgroundColor: 'rgba(0,0,0,0.8)',
+                    border: '1px solid rgba(255,255,255,0.2)',
+                    color: 'white'
+                  }}
+                  formatter={(value) => [`${value} 亿元`, '']}
+                />
+                <Bar
+                  dataKey="资产总额"
+                  fill="url(#assetTotalGradient)"
+                  radius={[2, 2, 0, 0]}
+                  barSize={35}
+                />
+                <Bar
+                  dataKey="资产净额"
+                  fill="url(#assetNetGradient)"
+                  radius={[2, 2, 0, 0]}
+                  barSize={35}
+                />
+                <defs>
+                  {/* 资产总额渐变 - 匹配BarElement的蓝色渐变 */}
+                  <linearGradient id="assetTotalGradient" x1="0" y1="0" x2="0" y2="1">
+                    <stop offset="0%" stopColor="#a2beff" stopOpacity={0.8}/>
+                    <stop offset="100%" stopColor="#a2beff" stopOpacity={0}/>
+                  </linearGradient>
+                  {/* 资产净额渐变 - 匹配BarElement的黄色渐变 */}
+                  <linearGradient id="assetNetGradient" x1="0" y1="0" x2="0" y2="1">
+                    <stop offset="0%" stopColor="#edce59" stopOpacity={0.8}/>
+                    <stop offset="100%" stopColor="#edce59" stopOpacity={0}/>
+                  </linearGradient>
+                </defs>
+              </BarChart>
+            </ResponsiveContainer>
           </div>
           </div>
         </div>
         </div>
 
 

+ 53 - 61
src/client/home/pages/FinancialDashboard/components/DebtRatioMetrics.tsx

@@ -1,6 +1,7 @@
 import BarElement from './BarElement';
 import BarElement from './BarElement';
 import BaseContainer from './BaseContainer';
 import BaseContainer from './BaseContainer';
 import ModuleHeader from './ModuleHeader';
 import ModuleHeader from './ModuleHeader';
+import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts';
 
 
 interface DebtRatioMetricsProps {
 interface DebtRatioMetricsProps {
   className?: string;
   className?: string;
@@ -14,14 +15,11 @@ const debtRatioData = [
   { year: '2025年', ratio: 49.82 }
   { year: '2025年', ratio: 49.82 }
 ];
 ];
 
 
-// 按照Figma设计规范中的精确柱形高度
-const barHeights = {
-  '2021年': { ratio: 110 },
-  '2022年': { ratio: 115 },
-  '2023年': { ratio: 77 },
-  '2024年': { ratio: 71 },
-  '2025年': { ratio: 75 }
-};
+// Recharts格式的数据
+const chartData = debtRatioData.map(item => ({
+  year: item.year,
+  '资产负债率': item.ratio
+}));
 
 
 export function DebtRatioMetrics({ className }: DebtRatioMetricsProps) {
 export function DebtRatioMetrics({ className }: DebtRatioMetricsProps) {
   return (
   return (
@@ -53,59 +51,53 @@ export function DebtRatioMetrics({ className }: DebtRatioMetricsProps) {
             单位:%
             单位:%
           </p>
           </p>
 
 
-          {/* 年份标签 */}
-          <div className="grid-cols-[max-content] grid-rows-[max-content] inline-grid justify-items-start leading-[0] relative shrink-0">
-            <div className="col-[1] grid-cols-[max-content] grid-rows-[max-content] inline-grid justify-items-start leading-[21.504px] ml-[93px] mt-[calc(50%+125.72px)] relative row-[1] text-[15px] text-[rgba(255,255,255,0.8)] whitespace-pre-wrap">
-              {debtRatioData.map((data, index) => (
-                <p
-                  key={data.year}
-                  className={`col-[1] mt-0 relative row-[1] ${
-                    index === 0 ? 'ml-0 w-[49.92px]' :
-                    index === 1 ? 'ml-[207.76px] text-right translate-x-[-100%] w-[53.76px]' :
-                    index === 2 ? 'ml-[376.68px] text-right translate-x-[-100%] w-[55.68px]' :
-                    index === 3 ? 'ml-[490px] w-[52.48px]' :
-                    'ml-[656px] mt-px w-[58px]'
-                  }`}
-                >
-                  {data.year}
-                </p>
-              ))}
-            </div>
-
-            {/* 网格线背景 */}
-            <div className="col-[1] grid-cols-[max-content] grid-rows-[max-content] inline-grid justify-items-start ml-[52px] mt-0 relative row-[1]">
-              {/* 水平网格线 */}
-              <div className="col-[1] h-[2px] ml-[0.12px] mt-0 relative row-[1] w-[794.879px] bg-gradient-to-r from-[rgba(255,255,255,0.1)] to-[rgba(255,255,255,0.05)]" />
-              <div className="col-[1] h-[2px] ml-0 mt-[40px] relative row-[1] w-[795px] bg-gradient-to-r from-[rgba(255,255,255,0.1)] to-[rgba(255,255,255,0.05)]" />
-              <div className="col-[1] h-[2px] ml-0 mt-[80px] relative row-[1] w-[795px] bg-gradient-to-r from-[rgba(255,255,255,0.1)] to-[rgba(255,255,255,0.05)]" />
-              <div className="col-[1] h-[2px] ml-0 mt-[120px] relative row-[1] w-[795px] bg-gradient-to-r from-[rgba(255,255,255,0.1)] to-[rgba(255,255,255,0.05)]" />
-              <div className="col-[1] h-[2px] ml-0 mt-[160px] relative row-[1] w-[795px] bg-gradient-to-r from-[rgba(255,255,255,0.1)] to-[rgba(255,255,255,0.05)]" />
-              <div className="col-[1] h-[2px] ml-0 mt-[200px] relative row-[1] w-[795.191px] bg-gradient-to-r from-[rgba(255,255,255,0.1)] to-[rgba(255,255,255,0.05)]" />
-              <div className="col-[1] h-[2px] ml-0 mt-[240px] relative row-[1] w-[795px] bg-gradient-to-r from-[rgba(255,255,255,0.1)] to-[rgba(255,255,255,0.05)]" />
-            </div>
-          </div>
-
-          {/* 柱形图数据 */}
-          <div className="box-border col-[1] content-stretch flex items-end justify-between ml-[69px] mt-[3.44px] relative row-[1] w-[753px]">
-            {debtRatioData.map((data) => (
-              <div key={data.year} className="content-stretch flex gap-[10px] items-end relative shrink-0">
-                {/* 资产负债率 */}
-                <div className="content-stretch flex flex-col gap-[10px] items-center relative shrink-0 w-[43px]">
-                  <p className="leading-[21.504px] min-w-full not-italic relative shrink-0 text-[13px] text-white w-[min-content] whitespace-pre-wrap">
-                    {data.ratio}
-                  </p>
-                  <div className="flex items-center justify-center relative shrink-0">
-                    <div className="flex-none rotate-[180deg]">
-                      <BarElement
-                        variant="debt-ratio"
-                        className={`w-[35px]`}
-                        style={{ height: `${barHeights[data.year as keyof typeof barHeights].ratio}px` }}
-                      />
-                    </div>
-                  </div>
-                </div>
-              </div>
-            ))}
+          {/* Recharts图表区域 */}
+          <div className="w-full h-[300px] mt-4">
+            <ResponsiveContainer width="100%" height="100%">
+              <BarChart
+                data={chartData}
+                margin={{ top: 20, right: 30, left: 20, bottom: 5 }}
+              >
+                <CartesianGrid
+                  strokeDasharray="3 3"
+                  stroke="rgba(255,255,255,0.1)"
+                  horizontal={true}
+                  vertical={false}
+                />
+                <XAxis
+                  dataKey="year"
+                  axisLine={false}
+                  tickLine={false}
+                  tick={{ fill: 'rgba(255,255,255,0.8)', fontSize: 14 }}
+                />
+                <YAxis
+                  axisLine={false}
+                  tickLine={false}
+                  tick={{ fill: 'rgba(255,255,255,0.8)', fontSize: 12 }}
+                />
+                <Tooltip
+                  contentStyle={{
+                    backgroundColor: 'rgba(0,0,0,0.8)',
+                    border: '1px solid rgba(255,255,255,0.2)',
+                    color: 'white'
+                  }}
+                  formatter={(value) => [`${value} %`, '']}
+                />
+                <Bar
+                  dataKey="资产负债率"
+                  fill="url(#debtRatioGradient)"
+                  radius={[2, 2, 0, 0]}
+                  barSize={35}
+                />
+                <defs>
+                  {/* 资产负债率渐变 - 匹配BarElement的蓝色渐变 */}
+                  <linearGradient id="debtRatioGradient" x1="0" y1="0" x2="0" y2="1">
+                    <stop offset="0%" stopColor="#a2beff" stopOpacity={0.8}/>
+                    <stop offset="100%" stopColor="#a2beff" stopOpacity={0}/>
+                  </linearGradient>
+                </defs>
+              </BarChart>
+            </ResponsiveContainer>
           </div>
           </div>
         </div>
         </div>
 
 

+ 53 - 61
src/client/home/pages/FinancialDashboard/components/IncomeMetrics.tsx

@@ -1,6 +1,7 @@
 import BarElement from './BarElement';
 import BarElement from './BarElement';
 import BaseContainer from './BaseContainer';
 import BaseContainer from './BaseContainer';
 import ModuleHeader from './ModuleHeader';
 import ModuleHeader from './ModuleHeader';
+import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts';
 
 
 interface IncomeMetricsProps {
 interface IncomeMetricsProps {
   className?: string;
   className?: string;
@@ -14,14 +15,11 @@ const incomeData = [
   { year: '2025年', income: 840.12 }
   { year: '2025年', income: 840.12 }
 ];
 ];
 
 
-// 按照Figma设计规范中的精确柱形高度
-const barHeights = {
-  '2021年': { income: 70 },
-  '2022年': { income: 100 },
-  '2023年': { income: 145 },
-  '2024年': { income: 175 },
-  '2025年': { income: 180 }
-};
+// Recharts格式的数据
+const chartData = incomeData.map(item => ({
+  year: item.year,
+  '收入': item.income
+}));
 
 
 export function IncomeMetrics({ className }: IncomeMetricsProps) {
 export function IncomeMetrics({ className }: IncomeMetricsProps) {
   return (
   return (
@@ -53,59 +51,53 @@ export function IncomeMetrics({ className }: IncomeMetricsProps) {
             单位:亿元
             单位:亿元
           </p>
           </p>
 
 
-          {/* 年份标签 */}
-          <div className="grid-cols-[max-content] grid-rows-[max-content] inline-grid justify-items-start leading-[0] relative shrink-0">
-            <div className="col-[1] grid-cols-[max-content] grid-rows-[max-content] inline-grid justify-items-start leading-[21.504px] ml-[93px] mt-[calc(50%+125.72px)] relative row-[1] text-[15px] text-[rgba(255,255,255,0.8)] whitespace-pre-wrap">
-              {incomeData.map((data, index) => (
-                <p
-                  key={data.year}
-                  className={`col-[1] mt-0 relative row-[1] ${
-                    index === 0 ? 'ml-0 w-[49.92px]' :
-                    index === 1 ? 'ml-[207.76px] text-right translate-x-[-100%] w-[53.76px]' :
-                    index === 2 ? 'ml-[376.68px] text-right translate-x-[-100%] w-[55.68px]' :
-                    index === 3 ? 'ml-[490px] w-[52.48px]' :
-                    'ml-[656px] mt-px w-[58px]'
-                  }`}
-                >
-                  {data.year}
-                </p>
-              ))}
-            </div>
-
-            {/* 网格线背景 */}
-            <div className="col-[1] grid-cols-[max-content] grid-rows-[max-content] inline-grid justify-items-start ml-[52px] mt-0 relative row-[1]">
-              {/* 水平网格线 */}
-              <div className="col-[1] h-[2px] ml-[0.12px] mt-0 relative row-[1] w-[794.879px] bg-gradient-to-r from-[rgba(255,255,255,0.1)] to-[rgba(255,255,255,0.05)]" />
-              <div className="col-[1] h-[2px] ml-0 mt-[40px] relative row-[1] w-[795px] bg-gradient-to-r from-[rgba(255,255,255,0.1)] to-[rgba(255,255,255,0.05)]" />
-              <div className="col-[1] h-[2px] ml-0 mt-[80px] relative row-[1] w-[795px] bg-gradient-to-r from-[rgba(255,255,255,0.1)] to-[rgba(255,255,255,0.05)]" />
-              <div className="col-[1] h-[2px] ml-0 mt-[120px] relative row-[1] w-[795px] bg-gradient-to-r from-[rgba(255,255,255,0.1)] to-[rgba(255,255,255,0.05)]" />
-              <div className="col-[1] h-[2px] ml-0 mt-[160px] relative row-[1] w-[795px] bg-gradient-to-r from-[rgba(255,255,255,0.1)] to-[rgba(255,255,255,0.05)]" />
-              <div className="col-[1] h-[2px] ml-0 mt-[200px] relative row-[1] w-[795.191px] bg-gradient-to-r from-[rgba(255,255,255,0.1)] to-[rgba(255,255,255,0.05)]" />
-              <div className="col-[1] h-[2px] ml-0 mt-[240px] relative row-[1] w-[795px] bg-gradient-to-r from-[rgba(255,255,255,0.1)] to-[rgba(255,255,255,0.05)]" />
-            </div>
-          </div>
-
-          {/* 柱形图数据 */}
-          <div className="box-border col-[1] content-stretch flex items-end justify-between ml-[69px] mt-[3.44px] relative row-[1] w-[753px]">
-            {incomeData.map((data) => (
-              <div key={data.year} className="content-stretch flex gap-[10px] items-end relative shrink-0">
-                {/* 收入 */}
-                <div className="content-stretch flex flex-col gap-[10px] items-center relative shrink-0 w-[43px]">
-                  <p className="leading-[21.504px] min-w-full not-italic relative shrink-0 text-[13px] text-white w-[min-content] whitespace-pre-wrap">
-                    {data.income}
-                  </p>
-                  <div className="flex items-center justify-center relative shrink-0">
-                    <div className="flex-none rotate-[180deg]">
-                      <BarElement
-                        variant="income"
-                        className={`w-[35px]`}
-                        style={{ height: `${barHeights[data.year as keyof typeof barHeights].income}px` }}
-                      />
-                    </div>
-                  </div>
-                </div>
-              </div>
-            ))}
+          {/* Recharts图表区域 */}
+          <div className="w-full h-[300px] mt-4">
+            <ResponsiveContainer width="100%" height="100%">
+              <BarChart
+                data={chartData}
+                margin={{ top: 20, right: 30, left: 20, bottom: 5 }}
+              >
+                <CartesianGrid
+                  strokeDasharray="3 3"
+                  stroke="rgba(255,255,255,0.1)"
+                  horizontal={true}
+                  vertical={false}
+                />
+                <XAxis
+                  dataKey="year"
+                  axisLine={false}
+                  tickLine={false}
+                  tick={{ fill: 'rgba(255,255,255,0.8)', fontSize: 14 }}
+                />
+                <YAxis
+                  axisLine={false}
+                  tickLine={false}
+                  tick={{ fill: 'rgba(255,255,255,0.8)', fontSize: 12 }}
+                />
+                <Tooltip
+                  contentStyle={{
+                    backgroundColor: 'rgba(0,0,0,0.8)',
+                    border: '1px solid rgba(255,255,255,0.2)',
+                    color: 'white'
+                  }}
+                  formatter={(value) => [`${value} 亿元`, '']}
+                />
+                <Bar
+                  dataKey="收入"
+                  fill="url(#incomeGradient)"
+                  radius={[2, 2, 0, 0]}
+                  barSize={35}
+                />
+                <defs>
+                  {/* 收入渐变 - 匹配BarElement的蓝色渐变 */}
+                  <linearGradient id="incomeGradient" x1="0" y1="0" x2="0" y2="1">
+                    <stop offset="0%" stopColor="#a2beff" stopOpacity={0.8}/>
+                    <stop offset="100%" stopColor="#a2beff" stopOpacity={0}/>
+                  </linearGradient>
+                </defs>
+              </BarChart>
+            </ResponsiveContainer>
           </div>
           </div>
         </div>
         </div>
 
 

+ 65 - 77
src/client/home/pages/FinancialDashboard/components/ProfitMetrics.tsx

@@ -2,6 +2,7 @@ import BarElement from './BarElement';
 import BaseContainer from './BaseContainer';
 import BaseContainer from './BaseContainer';
 import ModuleHeader from './ModuleHeader';
 import ModuleHeader from './ModuleHeader';
 import ModuleHeaderBackground from './ModuleHeaderBackground';
 import ModuleHeaderBackground from './ModuleHeaderBackground';
+import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts';
 
 
 interface ProfitMetricsProps {
 interface ProfitMetricsProps {
   className?: string;
   className?: string;
@@ -15,14 +16,12 @@ const profitData = [
   { year: '2025年', profitTotal: 1.34, profitNet: 1.00 }
   { year: '2025年', profitTotal: 1.34, profitNet: 1.00 }
 ];
 ];
 
 
-// 按照Figma设计规范中的精确柱形高度
-const barHeights = {
-  '2021年': { profitTotal: 60, profitNet: 68 },
-  '2022年': { profitTotal: 32, profitNet: 24 },
-  '2023年': { profitTotal: 51, profitNet: 24 },
-  '2024年': { profitTotal: 66, profitNet: 49 },
-  '2025年': { profitTotal: 54, profitNet: 40 }
-};
+// Recharts格式的数据
+const chartData = profitData.map(item => ({
+  year: item.year,
+  '利润总额': item.profitTotal,
+  '净利润': item.profitNet
+}));
 
 
 export function ProfitMetrics({ className }: ProfitMetricsProps) {
 export function ProfitMetrics({ className }: ProfitMetricsProps) {
   return (
   return (
@@ -64,75 +63,64 @@ export function ProfitMetrics({ className }: ProfitMetricsProps) {
             单位:亿元
             单位:亿元
           </p>
           </p>
 
 
-          {/* 年份标签 */}
-          <div className="grid-cols-[max-content] grid-rows-[max-content] inline-grid justify-items-start leading-[0] relative shrink-0">
-            <div className="col-[1] grid-cols-[max-content] grid-rows-[max-content] inline-grid justify-items-start leading-[21.504px] ml-[93px] mt-[calc(50%+125.72px)] relative row-[1] text-[15px] text-[rgba(255,255,255,0.8)] whitespace-pre-wrap">
-              {profitData.map((data, index) => (
-                <p
-                  key={data.year}
-                  className={`col-[1] mt-0 relative row-[1] ${
-                    index === 0 ? 'ml-0 w-[49.92px]' :
-                    index === 1 ? 'ml-[207.76px] text-right translate-x-[-100%] w-[53.76px]' :
-                    index === 2 ? 'ml-[376.68px] text-right translate-x-[-100%] w-[55.68px]' :
-                    index === 3 ? 'ml-[490px] w-[52.48px]' :
-                    'ml-[656px] mt-px w-[58px]'
-                  }`}
-                >
-                  {data.year}
-                </p>
-              ))}
-            </div>
-
-            {/* 网格线背景 */}
-            <div className="col-[1] grid-cols-[max-content] grid-rows-[max-content] inline-grid justify-items-start ml-[52px] mt-0 relative row-[1]">
-              {/* 水平网格线 */}
-              <div className="col-[1] h-[2px] ml-[0.12px] mt-0 relative row-[1] w-[794.879px] bg-gradient-to-r from-[rgba(255,255,255,0.1)] to-[rgba(255,255,255,0.05)]" />
-              <div className="col-[1] h-[2px] ml-0 mt-[40px] relative row-[1] w-[795px] bg-gradient-to-r from-[rgba(255,255,255,0.1)] to-[rgba(255,255,255,0.05)]" />
-              <div className="col-[1] h-[2px] ml-0 mt-[80px] relative row-[1] w-[795px] bg-gradient-to-r from-[rgba(255,255,255,0.1)] to-[rgba(255,255,255,0.05)]" />
-              <div className="col-[1] h-[2px] ml-0 mt-[120px] relative row-[1] w-[795px] bg-gradient-to-r from-[rgba(255,255,255,0.1)] to-[rgba(255,255,255,0.05)]" />
-              <div className="col-[1] h-[2px] ml-0 mt-[160px] relative row-[1] w-[795px] bg-gradient-to-r from-[rgba(255,255,255,0.1)] to-[rgba(255,255,255,0.05)]" />
-              <div className="col-[1] h-[2px] ml-0 mt-[200px] relative row-[1] w-[795.191px] bg-gradient-to-r from-[rgba(255,255,255,0.1)] to-[rgba(255,255,255,0.05)]" />
-              <div className="col-[1] h-[2px] ml-0 mt-[240px] relative row-[1] w-[795px] bg-gradient-to-r from-[rgba(255,255,255,0.1)] to-[rgba(255,255,255,0.05)]" />
-            </div>
-          </div>
-
-          {/* 柱形图数据 */}
-          <div className="box-border col-[1] content-stretch flex items-end justify-between ml-[69px] mt-[3.44px] relative row-[1] w-[753px]">
-            {profitData.map((data) => (
-              <div key={data.year} className="content-stretch flex gap-[10px] items-end relative shrink-0">
-                {/* 利润总额 */}
-                <div className="content-stretch flex flex-col gap-[10px] items-center relative shrink-0 w-[43px]">
-                  <p className="leading-[21.504px] min-w-full not-italic relative shrink-0 text-[13px] text-white w-[min-content] whitespace-pre-wrap">
-                    {data.profitTotal}
-                  </p>
-                  <div className="flex items-center justify-center relative shrink-0">
-                    <div className="flex-none rotate-[180deg]">
-                      <BarElement
-                        variant="profit-total"
-                        className={`w-[35px]`}
-                        style={{ height: `${barHeights[data.year as keyof typeof barHeights].profitTotal}px` }}
-                      />
-                    </div>
-                  </div>
-                </div>
-
-                {/* 净利润 */}
-                <div className="content-stretch flex flex-col gap-[10px] items-center relative shrink-0 w-[35px]">
-                  <p className="leading-[21.504px] min-w-full not-italic relative shrink-0 text-[13px] text-white w-[min-content] whitespace-pre-wrap">
-                    {data.profitNet}
-                  </p>
-                  <div className="flex items-center justify-center relative shrink-0">
-                    <div className="flex-none rotate-[180deg]">
-                      <BarElement
-                        variant="profit-net"
-                        className={`w-[35px] rounded-[2px]`}
-                        style={{ height: `${barHeights[data.year as keyof typeof barHeights].profitNet}px` }}
-                      />
-                    </div>
-                  </div>
-                </div>
-              </div>
-            ))}
+          {/* Recharts图表区域 */}
+          <div className="w-full h-[300px] mt-4">
+            <ResponsiveContainer width="100%" height="100%">
+              <BarChart
+                data={chartData}
+                margin={{ top: 20, right: 30, left: 20, bottom: 5 }}
+              >
+                <CartesianGrid
+                  strokeDasharray="3 3"
+                  stroke="rgba(255,255,255,0.1)"
+                  horizontal={true}
+                  vertical={false}
+                />
+                <XAxis
+                  dataKey="year"
+                  axisLine={false}
+                  tickLine={false}
+                  tick={{ fill: 'rgba(255,255,255,0.8)', fontSize: 14 }}
+                />
+                <YAxis
+                  axisLine={false}
+                  tickLine={false}
+                  tick={{ fill: 'rgba(255,255,255,0.8)', fontSize: 12 }}
+                />
+                <Tooltip
+                  contentStyle={{
+                    backgroundColor: 'rgba(0,0,0,0.8)',
+                    border: '1px solid rgba(255,255,255,0.2)',
+                    color: 'white'
+                  }}
+                  formatter={(value) => [`${value} 亿元`, '']}
+                />
+                <Bar
+                  dataKey="利润总额"
+                  fill="url(#profitTotalGradient)"
+                  radius={[2, 2, 0, 0]}
+                  barSize={35}
+                />
+                <Bar
+                  dataKey="净利润"
+                  fill="url(#profitNetGradient)"
+                  radius={[2, 2, 0, 0]}
+                  barSize={35}
+                />
+                <defs>
+                  {/* 利润总额渐变 - 匹配figma-jsx.md的黄色渐变 */}
+                  <linearGradient id="profitTotalGradient" x1="0" y1="0" x2="0" y2="1">
+                    <stop offset="0%" stopColor="#feffc2" stopOpacity={0.8}/>
+                    <stop offset="100%" stopColor="#feffc2" stopOpacity={0}/>
+                  </linearGradient>
+                  {/* 净利润渐变 - 匹配figma-jsx.md的紫色渐变 */}
+                  <linearGradient id="profitNetGradient" x1="0" y1="0" x2="0" y2="1">
+                    <stop offset="0%" stopColor="#dccfff" stopOpacity={0.8}/>
+                    <stop offset="100%" stopColor="#dccfff" stopOpacity={0}/>
+                  </linearGradient>
+                </defs>
+              </BarChart>
+            </ResponsiveContainer>
           </div>
           </div>
         </div>
         </div>