- 新增 index.tsx: 主组件(组合层,50 行) - 新增 CompanyHeaderCard.tsx: 头部卡片组件(168 行) - 新增 hooks/useCompanyOverviewData.ts: 数据加载 Hook - 删除 index.js: 原 330 行代码精简 85% - 修复 Company/index.js: 恢复 CompanyTabs 渲染 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
141 lines
4.8 KiB
TypeScript
141 lines
4.8 KiB
TypeScript
// src/views/Company/components/CompanyOverview/hooks/useCompanyOverviewData.ts
|
||
// 公司概览数据加载 Hook
|
||
|
||
import { useState, useEffect, useCallback } from "react";
|
||
import { logger } from "@utils/logger";
|
||
import { getApiBase } from "@utils/apiConfig";
|
||
import type {
|
||
BasicInfo,
|
||
ActualControl,
|
||
Concentration,
|
||
Management,
|
||
Shareholder,
|
||
Branch,
|
||
Announcement,
|
||
DisclosureSchedule,
|
||
CompanyOverviewData,
|
||
} from "../types";
|
||
|
||
const API_BASE_URL = getApiBase();
|
||
|
||
interface ApiResponse<T> {
|
||
success: boolean;
|
||
data: T;
|
||
}
|
||
|
||
/**
|
||
* 公司概览数据加载 Hook
|
||
* @param propStockCode - 股票代码
|
||
* @returns 公司概览数据
|
||
*/
|
||
export const useCompanyOverviewData = (propStockCode?: string): CompanyOverviewData => {
|
||
const [stockCode, setStockCode] = useState(propStockCode || "000001");
|
||
const [loading, setLoading] = useState(false);
|
||
const [dataLoaded, setDataLoaded] = useState(false);
|
||
|
||
// 基本信息数据
|
||
const [basicInfo, setBasicInfo] = useState<BasicInfo | null>(null);
|
||
const [actualControl, setActualControl] = useState<ActualControl[]>([]);
|
||
const [concentration, setConcentration] = useState<Concentration[]>([]);
|
||
const [management, setManagement] = useState<Management[]>([]);
|
||
const [topCirculationShareholders, setTopCirculationShareholders] = useState<Shareholder[]>([]);
|
||
const [topShareholders, setTopShareholders] = useState<Shareholder[]>([]);
|
||
const [branches, setBranches] = useState<Branch[]>([]);
|
||
const [announcements, setAnnouncements] = useState<Announcement[]>([]);
|
||
const [disclosureSchedule, setDisclosureSchedule] = useState<DisclosureSchedule[]>([]);
|
||
|
||
// 监听 props 中的 stockCode 变化
|
||
useEffect(() => {
|
||
if (propStockCode && propStockCode !== stockCode) {
|
||
setStockCode(propStockCode);
|
||
setDataLoaded(false);
|
||
}
|
||
}, [propStockCode, stockCode]);
|
||
|
||
// 加载基本信息数据(9个接口)
|
||
const loadBasicInfoData = useCallback(async () => {
|
||
if (dataLoaded) return;
|
||
|
||
setLoading(true);
|
||
|
||
try {
|
||
const [
|
||
basicRes,
|
||
actualRes,
|
||
concentrationRes,
|
||
managementRes,
|
||
circulationRes,
|
||
shareholdersRes,
|
||
branchesRes,
|
||
announcementsRes,
|
||
disclosureRes,
|
||
] = await Promise.all([
|
||
fetch(`${API_BASE_URL}/api/stock/${stockCode}/basic-info`).then((r) =>
|
||
r.json()
|
||
) as Promise<ApiResponse<BasicInfo>>,
|
||
fetch(`${API_BASE_URL}/api/stock/${stockCode}/actual-control`).then((r) =>
|
||
r.json()
|
||
) as Promise<ApiResponse<ActualControl[]>>,
|
||
fetch(`${API_BASE_URL}/api/stock/${stockCode}/concentration`).then((r) =>
|
||
r.json()
|
||
) as Promise<ApiResponse<Concentration[]>>,
|
||
fetch(`${API_BASE_URL}/api/stock/${stockCode}/management?active_only=true`).then((r) =>
|
||
r.json()
|
||
) as Promise<ApiResponse<Management[]>>,
|
||
fetch(`${API_BASE_URL}/api/stock/${stockCode}/top-circulation-shareholders?limit=10`).then((r) =>
|
||
r.json()
|
||
) as Promise<ApiResponse<Shareholder[]>>,
|
||
fetch(`${API_BASE_URL}/api/stock/${stockCode}/top-shareholders?limit=10`).then((r) =>
|
||
r.json()
|
||
) as Promise<ApiResponse<Shareholder[]>>,
|
||
fetch(`${API_BASE_URL}/api/stock/${stockCode}/branches`).then((r) =>
|
||
r.json()
|
||
) as Promise<ApiResponse<Branch[]>>,
|
||
fetch(`${API_BASE_URL}/api/stock/${stockCode}/announcements?limit=20`).then((r) =>
|
||
r.json()
|
||
) as Promise<ApiResponse<Announcement[]>>,
|
||
fetch(`${API_BASE_URL}/api/stock/${stockCode}/disclosure-schedule`).then((r) =>
|
||
r.json()
|
||
) as Promise<ApiResponse<DisclosureSchedule[]>>,
|
||
]);
|
||
|
||
if (basicRes.success) setBasicInfo(basicRes.data);
|
||
if (actualRes.success) setActualControl(actualRes.data);
|
||
if (concentrationRes.success) setConcentration(concentrationRes.data);
|
||
if (managementRes.success) setManagement(managementRes.data);
|
||
if (circulationRes.success) setTopCirculationShareholders(circulationRes.data);
|
||
if (shareholdersRes.success) setTopShareholders(shareholdersRes.data);
|
||
if (branchesRes.success) setBranches(branchesRes.data);
|
||
if (announcementsRes.success) setAnnouncements(announcementsRes.data);
|
||
if (disclosureRes.success) setDisclosureSchedule(disclosureRes.data);
|
||
|
||
setDataLoaded(true);
|
||
} catch (err) {
|
||
logger.error("useCompanyOverviewData", "loadBasicInfoData", err, { stockCode });
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
}, [stockCode, dataLoaded]);
|
||
|
||
// 首次加载
|
||
useEffect(() => {
|
||
if (stockCode) {
|
||
loadBasicInfoData();
|
||
}
|
||
}, [stockCode, loadBasicInfoData]);
|
||
|
||
return {
|
||
basicInfo,
|
||
actualControl,
|
||
concentration,
|
||
management,
|
||
topCirculationShareholders,
|
||
topShareholders,
|
||
branches,
|
||
announcements,
|
||
disclosureSchedule,
|
||
loading,
|
||
dataLoaded,
|
||
};
|
||
};
|