Преглед изворни кода

✨ feat(financial-dashboard): 实现财务仪表盘数据展示功能

- 添加财务数据接口调用逻辑,使用react-query管理数据获取状态
- 实现加载中、错误和空数据状态的显示处理
- 为资产、利润、收入和负债率组件添加数据接收和展示逻辑
- 将原始财务数据单位从元转换为亿元进行展示
- 各子组件添加空数据状态处理

✨ feat(asset-metrics): 添加资产数据展示功能

- 实现资产数据接收和展示逻辑
- 添加空数据状态显示
- 将资产数据从元转换为亿元进行展示

✨ feat(income-metrics): 添加收入数据展示功能

- 实现收入数据接收和展示逻辑
- 添加空数据状态显示
- 将收入数据从元转换为亿元进行展示

✨ feat(profit-metrics): 添加利润数据展示功能

- 实现利润数据接收和展示逻辑
- 添加空数据状态显示
- 将利润数据从元转换为亿元进行展示
yourname пре 2 месеци
родитељ
комит
0245c469b6

+ 97 - 5
src/client/home/pages/FinancialDashboard/FinancialDashboard.tsx

@@ -1,13 +1,65 @@
-import React, { useState } from 'react';
+import React, { useState, useEffect } from 'react';
+import { useQuery } from '@tanstack/react-query';
 import { AssetMetrics } from './components/AssetMetrics';
 import { ProfitMetrics } from './components/ProfitMetrics';
 import { IncomeMetrics } from './components/IncomeMetrics';
 import { DebtRatioMetrics } from './components/DebtRatioMetrics';
 import { VariationModal } from './components/VariationModal';
+import { client } from '@/api';
+
+interface FinancialData {
+  code: number;
+  msg: string;
+  rows: Array<{
+    assetTotalNet: Array<{
+      id: number;
+      year: number;
+      assetTotal: number;
+      assetNet: number;
+      dataDeadline: string;
+      createTime: string;
+      updateTime: string;
+    }>;
+    profitTotalNet: Array<{
+      id: number;
+      year: number;
+      profitTotal: number;
+      profitNet: number;
+      dataDeadline: string;
+      createTime: string;
+      updateTime: string;
+    }>;
+    incomeStatement: Array<{
+      id: number;
+      year: number;
+      income: number;
+      dataDeadline: string;
+      createTime: string;
+      updateTime: string;
+    }>;
+    assetLiabilityRatio: Array<{
+      id: number;
+      year: number;
+      assetLiabilityRatio: number;
+      dataDeadline: string;
+      createTime: string;
+      updateTime: string;
+    }>;
+  }>;
+}
 
 export function FinancialDashboard() {
   const [isModalOpen, setIsModalOpen] = useState(false);
 
+  // 获取财务数据
+  const { data: financialData, isLoading, error } = useQuery({
+    queryKey: ['financial-data'],
+    queryFn: async () => {
+      const response = await client.dash.outlook.$get();
+      return response.json() as Promise<FinancialData>;
+    },
+  });
+
   const handleOpenModal = () => {
     setIsModalOpen(true);
   };
@@ -16,6 +68,46 @@ export function FinancialDashboard() {
     setIsModalOpen(false);
   };
 
+  // 加载状态
+  if (isLoading) {
+    return (
+      <div className="min-h-screen bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 text-white p-6 flex items-center justify-center">
+        <div className="text-center">
+          <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-white mx-auto mb-4"></div>
+          <p className="text-lg">加载财务数据中...</p>
+        </div>
+      </div>
+    );
+  }
+
+  // 错误状态
+  if (error) {
+    return (
+      <div className="min-h-screen bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 text-white p-6 flex items-center justify-center">
+        <div className="text-center">
+          <div className="text-red-400 text-4xl mb-4">⚠️</div>
+          <h2 className="text-xl font-bold mb-2">数据加载失败</h2>
+          <p className="text-slate-300">无法获取财务数据,请稍后重试</p>
+        </div>
+      </div>
+    );
+  }
+
+  // 数据为空状态
+  if (!financialData || !financialData.rows || financialData.rows.length === 0) {
+    return (
+      <div className="min-h-screen bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 text-white p-6 flex items-center justify-center">
+        <div className="text-center">
+          <div className="text-slate-400 text-4xl mb-4">📊</div>
+          <h2 className="text-xl font-bold mb-2">暂无财务数据</h2>
+          <p className="text-slate-300">当前没有可显示的财务数据</p>
+        </div>
+      </div>
+    );
+  }
+
+  const financialRow = financialData.rows[0];
+
   return (
     <div className="min-h-screen bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 text-white p-6">
       {/* 页面标题 */}
@@ -29,22 +121,22 @@ export function FinancialDashboard() {
       <div className="grid grid-cols-1 lg:grid-cols-2 gap-6 max-w-7xl mx-auto">
         {/* 资产负债率模块 */}
         <div className="bg-slate-800/50 backdrop-blur-sm rounded-xl border border-slate-700/50 p-6">
-          <AssetMetrics />
+          <AssetMetrics data={financialRow.assetTotalNet} />
         </div>
 
         {/* 利润总额与净利润模块 */}
         <div className="bg-slate-800/50 backdrop-blur-sm rounded-xl border border-slate-700/50 p-6">
-          <ProfitMetrics />
+          <ProfitMetrics data={financialRow.profitTotalNet} />
         </div>
 
         {/* 收入模块 */}
         <div className="bg-slate-800/50 backdrop-blur-sm rounded-xl border border-slate-700/50 p-6">
-          <IncomeMetrics />
+          <IncomeMetrics data={financialRow.incomeStatement} />
         </div>
 
         {/* 资产负债率(百分比)模块 */}
         <div className="bg-slate-800/50 backdrop-blur-sm rounded-xl border border-slate-700/50 p-6">
-          <DebtRatioMetrics />
+          <DebtRatioMetrics data={financialRow.assetLiabilityRatio} />
         </div>
       </div>
 

+ 35 - 10
src/client/home/pages/FinancialDashboard/components/AssetMetrics.tsx

@@ -1,15 +1,40 @@
 import React from 'react';
 
-export function AssetMetrics() {
-  const data = [
-    { year: '2021年', totalAssets: 200.46, netAssets: 55.40 },
-    { year: '2022年', totalAssets: 243.27, netAssets: 57.49 },
-    { year: '2023年', totalAssets: 509.08, netAssets: 247.29 },
-    { year: '2024年', totalAssets: 772.66, netAssets: 407.68 },
-    { year: '2025年', totalAssets: 840.12, netAssets: 421.55 },
-  ];
+interface AssetData {
+  id: number;
+  year: number;
+  assetTotal: number;
+  assetNet: number;
+  dataDeadline: string;
+  createTime: string;
+  updateTime: string;
+}
 
-  const maxValue = Math.max(...data.map(d => Math.max(d.totalAssets, d.netAssets)));
+interface AssetMetricsProps {
+  data: AssetData[];
+}
+
+export function AssetMetrics({ data }: AssetMetricsProps) {
+  // 如果没有数据,显示空状态
+  if (!data || data.length === 0) {
+    return (
+      <div className="h-full flex items-center justify-center">
+        <div className="text-center text-slate-400">
+          <div className="text-2xl mb-2">📊</div>
+          <p>暂无资产数据</p>
+        </div>
+      </div>
+    );
+  }
+
+  // 将数据转换为显示格式,将元转换为亿元
+  const displayData = data.map(item => ({
+    year: `${item.year}年`,
+    totalAssets: item.assetTotal / 100000000, // 转换为亿元
+    netAssets: item.assetNet / 100000000,     // 转换为亿元
+  }));
+
+  const maxValue = Math.max(...displayData.map(d => Math.max(d.totalAssets, d.netAssets)));
 
   return (
     <div className="h-full">
@@ -53,7 +78,7 @@ export function AssetMetrics() {
 
           {/* 数据条 */}
           <div className="flex items-end justify-between h-full px-4">
-            {data.map((item, index) => (
+            {displayData.map((item, index) => (
               <div key={item.year} className="flex flex-col items-center gap-2">
                 {/* 数据标签 */}
                 <div className="text-center">

+ 32 - 9
src/client/home/pages/FinancialDashboard/components/IncomeMetrics.tsx

@@ -1,15 +1,38 @@
 import React from 'react';
 
-export function IncomeMetrics() {
-  const data = [
-    { year: '2021年', income: 161.29 },
-    { year: '2022年', income: 243.27 },
-    { year: '2023年', income: 509.08 },
-    { year: '2024年', income: 772.66 },
-    { year: '2025年', income: 840.12 },
-  ];
+interface IncomeData {
+  id: number;
+  year: number;
+  income: number;
+  dataDeadline: string;
+  createTime: string;
+  updateTime: string;
+}
 
-  const maxValue = Math.max(...data.map(d => d.income));
+interface IncomeMetricsProps {
+  data: IncomeData[];
+}
+
+export function IncomeMetrics({ data }: IncomeMetricsProps) {
+  // 如果没有数据,显示空状态
+  if (!data || data.length === 0) {
+    return (
+      <div className="h-full flex items-center justify-center">
+        <div className="text-center text-slate-400">
+          <div className="text-2xl mb-2">📊</div>
+          <p>暂无收入数据</p>
+        </div>
+      </div>
+    );
+  }
+
+  // 将数据转换为显示格式,将元转换为亿元
+  const displayData = data.map(item => ({
+    year: `${item.year}年`,
+    income: item.income / 100000000, // 转换为亿元
+  }));
+
+  const maxValue = Math.max(...displayData.map(d => d.income));
 
   return (
     <div className="h-full">

+ 36 - 11
src/client/home/pages/FinancialDashboard/components/ProfitMetrics.tsx

@@ -1,16 +1,41 @@
 import React from 'react';
 
-export function ProfitMetrics() {
-  const data = [
-    { year: '2021年', totalProfit: -1.5, netProfit: -1.7 },
-    { year: '2022年', totalProfit: 0.8, netProfit: 0.59 },
-    { year: '2023年', totalProfit: 1.28, netProfit: 0.6 },
-    { year: '2024年', totalProfit: 1.65, netProfit: 1.22 },
-    { year: '2025年', totalProfit: 1.34, netProfit: 1.00 },
-  ];
+interface ProfitData {
+  id: number;
+  year: number;
+  profitTotal: number;
+  profitNet: number;
+  dataDeadline: string;
+  createTime: string;
+  updateTime: string;
+}
 
-  const maxPositive = Math.max(...data.map(d => Math.max(d.totalProfit, d.netProfit)).filter(v => v > 0));
-  const maxNegative = Math.min(...data.map(d => Math.min(d.totalProfit, d.netProfit)).filter(v => v < 0));
+interface ProfitMetricsProps {
+  data: ProfitData[];
+}
+
+export function ProfitMetrics({ data }: ProfitMetricsProps) {
+  // 如果没有数据,显示空状态
+  if (!data || data.length === 0) {
+    return (
+      <div className="h-full flex items-center justify-center">
+        <div className="text-center text-slate-400">
+          <div className="text-2xl mb-2">📊</div>
+          <p>暂无利润数据</p>
+        </div>
+      </div>
+    );
+  }
+
+  // 将数据转换为显示格式,将元转换为亿元
+  const displayData = data.map(item => ({
+    year: `${item.year}年`,
+    totalProfit: item.profitTotal / 100000000, // 转换为亿元
+    netProfit: item.profitNet / 100000000,     // 转换为亿元
+  }));
+
+  const maxPositive = Math.max(...displayData.map(d => Math.max(d.totalProfit, d.netProfit)).filter(v => v > 0));
+  const maxNegative = Math.min(...displayData.map(d => Math.min(d.totalProfit, d.netProfit)).filter(v => v < 0));
 
   return (
     <div className="h-full">
@@ -54,7 +79,7 @@ export function ProfitMetrics() {
 
           {/* 数据条 */}
           <div className="flex items-end justify-between h-full px-4">
-            {data.map((item, index) => (
+            {displayData.map((item, index) => (
               <div key={item.year} className="flex flex-col items-center gap-2">
                 {/* 数据标签 */}
                 <div className="text-center">