Compare commits

...

3 Commits

Author SHA1 Message Date
zdl
385d452f5a chore(CompanyOverview): 移除未使用的 CompanyOverviewData 类型定义
useCompanyOverviewData hook 已在 axios 迁移中删除,
对应的类型定义也应清理

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-17 12:46:16 +08:00
zdl
bdc823e122 fix(CompanyOverview): 修复 useBasicInfo 重复调用问题
- BusinessInfoPanel: 改为内部调用 useBasicInfo,自行获取数据
- BasicInfoTab: 移除 basicInfo prop 传递
- CompanyOverview: 移除顶层 useBasicInfo 调用
- types.ts: 补充 BasicInfo 工商信息字段类型定义

修复前:CompanyOverview 和各子组件重复请求 /api/stock/{code}/basic-info
修复后:仅 BusinessInfoPanel 在需要时请求一次

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-17 12:02:31 +08:00
zdl
c83d239219 refactor(Company): fetch 请求迁移至 axios
- DeepAnalysis: 4 个 fetch → axios
- DynamicTracking: 3 个 fetch → axios (NewsPanel, ForecastPanel)
- MarketDataView/services: 4 个 fetch → axios
- CompanyOverview/hooks: 9 个 fetch → axios (6 个文件)
- StockQuoteCard/hooks: 1 个 fetch → axios
- ValueChainNodeCard: 1 个 fetch → axios

清理:
- 删除未使用的 useCompanyOverviewData.ts
- 移除所有 getApiBase/API_BASE_URL 引用

总计: 22 个 fetch 调用迁移, 复用项目已有的 axios 拦截器配置

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-17 11:54:32 +08:00
17 changed files with 89 additions and 287 deletions

View File

@@ -12,15 +12,27 @@ import {
Divider, Divider,
Center, Center,
Code, Code,
Spinner,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import { THEME } from "../config"; import { THEME } from "../config";
import { useBasicInfo } from "../../hooks/useBasicInfo";
interface BusinessInfoPanelProps { interface BusinessInfoPanelProps {
basicInfo: any; stockCode: string;
} }
const BusinessInfoPanel: React.FC<BusinessInfoPanelProps> = ({ basicInfo }) => { const BusinessInfoPanel: React.FC<BusinessInfoPanelProps> = ({ stockCode }) => {
const { basicInfo, loading } = useBasicInfo(stockCode);
if (loading) {
return (
<Center h="200px">
<Spinner size="lg" color={THEME.gold} />
</Center>
);
}
if (!basicInfo) { if (!basicInfo) {
return ( return (
<Center h="200px"> <Center h="200px">

View File

@@ -17,7 +17,6 @@ import {
// Props 类型定义 // Props 类型定义
export interface BasicInfoTabProps { export interface BasicInfoTabProps {
stockCode: string; stockCode: string;
basicInfo?: any;
// 可配置项 // 可配置项
enabledTabs?: string[]; // 指定显示哪些 Tab通过 key enabledTabs?: string[]; // 指定显示哪些 Tab通过 key
@@ -59,7 +58,6 @@ const buildTabsConfig = (enabledKeys?: string[]): SubTabConfig[] => {
*/ */
const BasicInfoTab: React.FC<BasicInfoTabProps> = ({ const BasicInfoTab: React.FC<BasicInfoTabProps> = ({
stockCode, stockCode,
basicInfo,
enabledTabs, enabledTabs,
defaultTabIndex = 0, defaultTabIndex = 0,
onTabChange, onTabChange,
@@ -72,7 +70,7 @@ const BasicInfoTab: React.FC<BasicInfoTabProps> = ({
<CardBody p={0}> <CardBody p={0}>
<SubTabContainer <SubTabContainer
tabs={tabs} tabs={tabs}
componentProps={{ stockCode, basicInfo }} componentProps={{ stockCode }}
defaultIndex={defaultTabIndex} defaultIndex={defaultTabIndex}
onTabChange={onTabChange} onTabChange={onTabChange}
themePreset="blackGold" themePreset="blackGold"

View File

@@ -32,12 +32,10 @@ import {
FaStar, FaStar,
} from 'react-icons/fa'; } from 'react-icons/fa';
import { logger } from '@utils/logger'; import { logger } from '@utils/logger';
import { getApiBase } from '@utils/apiConfig'; import axios from '@utils/axiosConfig';
import RelatedCompaniesModal from './RelatedCompaniesModal'; import RelatedCompaniesModal from './RelatedCompaniesModal';
import type { ValueChainNodeCardProps, RelatedCompany } from '../../types'; import type { ValueChainNodeCardProps, RelatedCompany } from '../../types';
const API_BASE_URL = getApiBase();
// 黑金主题配置 // 黑金主题配置
const THEME = { const THEME = {
cardBg: 'gray.700', cardBg: 'gray.700',
@@ -120,12 +118,11 @@ const ValueChainNodeCard: React.FC<ValueChainNodeCardProps> = memo(({
const fetchRelatedCompanies = async () => { const fetchRelatedCompanies = async () => {
setLoadingRelated(true); setLoadingRelated(true);
try { try {
const response = await fetch( const { data } = await axios.get(
`${API_BASE_URL}/api/company/value-chain/related-companies?node_name=${encodeURIComponent( `/api/company/value-chain/related-companies?node_name=${encodeURIComponent(
node.node_name node.node_name
)}` )}`
); );
const data = await response.json();
if (data.success) { if (data.success) {
setRelatedCompanies(data.data || []); setRelatedCompanies(data.data || []);
} else { } else {

View File

@@ -3,11 +3,9 @@
import { useState, useEffect, useCallback } from "react"; import { useState, useEffect, useCallback } from "react";
import { logger } from "@utils/logger"; import { logger } from "@utils/logger";
import { getApiBase } from "@utils/apiConfig"; import axios from "@utils/axiosConfig";
import type { Announcement } from "../types"; import type { Announcement } from "../types";
const API_BASE_URL = getApiBase();
interface ApiResponse<T> { interface ApiResponse<T> {
success: boolean; success: boolean;
data: T; data: T;
@@ -35,10 +33,9 @@ export const useAnnouncementsData = (stockCode?: string): UseAnnouncementsDataRe
setError(null); setError(null);
try { try {
const response = await fetch( const { data: result } = await axios.get<ApiResponse<Announcement[]>>(
`${API_BASE_URL}/api/stock/${stockCode}/announcements?limit=20` `/api/stock/${stockCode}/announcements?limit=20`
); );
const result = (await response.json()) as ApiResponse<Announcement[]>;
if (result.success) { if (result.success) {
setAnnouncements(result.data); setAnnouncements(result.data);

View File

@@ -3,11 +3,9 @@
import { useState, useEffect, useCallback } from "react"; import { useState, useEffect, useCallback } from "react";
import { logger } from "@utils/logger"; import { logger } from "@utils/logger";
import { getApiBase } from "@utils/apiConfig"; import axios from "@utils/axiosConfig";
import type { BasicInfo } from "../types"; import type { BasicInfo } from "../types";
const API_BASE_URL = getApiBase();
interface ApiResponse<T> { interface ApiResponse<T> {
success: boolean; success: boolean;
data: T; data: T;
@@ -35,8 +33,9 @@ export const useBasicInfo = (stockCode?: string): UseBasicInfoResult => {
setError(null); setError(null);
try { try {
const response = await fetch(`${API_BASE_URL}/api/stock/${stockCode}/basic-info`); const { data: result } = await axios.get<ApiResponse<BasicInfo>>(
const result = (await response.json()) as ApiResponse<BasicInfo>; `/api/stock/${stockCode}/basic-info`
);
if (result.success) { if (result.success) {
setBasicInfo(result.data); setBasicInfo(result.data);

View File

@@ -3,11 +3,9 @@
import { useState, useEffect, useCallback } from "react"; import { useState, useEffect, useCallback } from "react";
import { logger } from "@utils/logger"; import { logger } from "@utils/logger";
import { getApiBase } from "@utils/apiConfig"; import axios from "@utils/axiosConfig";
import type { Branch } from "../types"; import type { Branch } from "../types";
const API_BASE_URL = getApiBase();
interface ApiResponse<T> { interface ApiResponse<T> {
success: boolean; success: boolean;
data: T; data: T;
@@ -35,8 +33,9 @@ export const useBranchesData = (stockCode?: string): UseBranchesDataResult => {
setError(null); setError(null);
try { try {
const response = await fetch(`${API_BASE_URL}/api/stock/${stockCode}/branches`); const { data: result } = await axios.get<ApiResponse<Branch[]>>(
const result = (await response.json()) as ApiResponse<Branch[]>; `/api/stock/${stockCode}/branches`
);
if (result.success) { if (result.success) {
setBranches(result.data); setBranches(result.data);

View File

@@ -1,140 +0,0 @@
// 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,
};
};

View File

@@ -3,11 +3,9 @@
import { useState, useEffect, useCallback } from "react"; import { useState, useEffect, useCallback } from "react";
import { logger } from "@utils/logger"; import { logger } from "@utils/logger";
import { getApiBase } from "@utils/apiConfig"; import axios from "@utils/axiosConfig";
import type { DisclosureSchedule } from "../types"; import type { DisclosureSchedule } from "../types";
const API_BASE_URL = getApiBase();
interface ApiResponse<T> { interface ApiResponse<T> {
success: boolean; success: boolean;
data: T; data: T;
@@ -35,10 +33,9 @@ export const useDisclosureData = (stockCode?: string): UseDisclosureDataResult =
setError(null); setError(null);
try { try {
const response = await fetch( const { data: result } = await axios.get<ApiResponse<DisclosureSchedule[]>>(
`${API_BASE_URL}/api/stock/${stockCode}/disclosure-schedule` `/api/stock/${stockCode}/disclosure-schedule`
); );
const result = (await response.json()) as ApiResponse<DisclosureSchedule[]>;
if (result.success) { if (result.success) {
setDisclosureSchedule(result.data); setDisclosureSchedule(result.data);

View File

@@ -3,11 +3,9 @@
import { useState, useEffect, useCallback } from "react"; import { useState, useEffect, useCallback } from "react";
import { logger } from "@utils/logger"; import { logger } from "@utils/logger";
import { getApiBase } from "@utils/apiConfig"; import axios from "@utils/axiosConfig";
import type { Management } from "../types"; import type { Management } from "../types";
const API_BASE_URL = getApiBase();
interface ApiResponse<T> { interface ApiResponse<T> {
success: boolean; success: boolean;
data: T; data: T;
@@ -35,10 +33,9 @@ export const useManagementData = (stockCode?: string): UseManagementDataResult =
setError(null); setError(null);
try { try {
const response = await fetch( const { data: result } = await axios.get<ApiResponse<Management[]>>(
`${API_BASE_URL}/api/stock/${stockCode}/management?active_only=true` `/api/stock/${stockCode}/management?active_only=true`
); );
const result = (await response.json()) as ApiResponse<Management[]>;
if (result.success) { if (result.success) {
setManagement(result.data); setManagement(result.data);

View File

@@ -3,11 +3,9 @@
import { useState, useEffect, useCallback } from "react"; import { useState, useEffect, useCallback } from "react";
import { logger } from "@utils/logger"; import { logger } from "@utils/logger";
import { getApiBase } from "@utils/apiConfig"; import axios from "@utils/axiosConfig";
import type { ActualControl, Concentration, Shareholder } from "../types"; import type { ActualControl, Concentration, Shareholder } from "../types";
const API_BASE_URL = getApiBase();
interface ApiResponse<T> { interface ApiResponse<T> {
success: boolean; success: boolean;
data: T; data: T;
@@ -41,19 +39,16 @@ export const useShareholderData = (stockCode?: string): UseShareholderDataResult
setError(null); setError(null);
try { try {
const [actualRes, concentrationRes, shareholdersRes, circulationRes] = await Promise.all([ const [
fetch(`${API_BASE_URL}/api/stock/${stockCode}/actual-control`).then((r) => { data: actualRes },
r.json() { data: concentrationRes },
) as Promise<ApiResponse<ActualControl[]>>, { data: shareholdersRes },
fetch(`${API_BASE_URL}/api/stock/${stockCode}/concentration`).then((r) => { data: circulationRes },
r.json() ] = await Promise.all([
) as Promise<ApiResponse<Concentration[]>>, axios.get<ApiResponse<ActualControl[]>>(`/api/stock/${stockCode}/actual-control`),
fetch(`${API_BASE_URL}/api/stock/${stockCode}/top-shareholders?limit=10`).then((r) => axios.get<ApiResponse<Concentration[]>>(`/api/stock/${stockCode}/concentration`),
r.json() axios.get<ApiResponse<Shareholder[]>>(`/api/stock/${stockCode}/top-shareholders?limit=10`),
) as Promise<ApiResponse<Shareholder[]>>, axios.get<ApiResponse<Shareholder[]>>(`/api/stock/${stockCode}/top-circulation-shareholders?limit=10`),
fetch(`${API_BASE_URL}/api/stock/${stockCode}/top-circulation-shareholders?limit=10`).then((r) =>
r.json()
) as Promise<ApiResponse<Shareholder[]>>,
]); ]);
if (actualRes.success) setActualControl(actualRes.data); if (actualRes.success) setActualControl(actualRes.data);

View File

@@ -4,10 +4,9 @@
import React from "react"; import React from "react";
import { VStack } from "@chakra-ui/react"; import { VStack } from "@chakra-ui/react";
import { useBasicInfo } from "./hooks/useBasicInfo";
import type { CompanyOverviewProps } from "./types"; import type { CompanyOverviewProps } from "./types";
// 子组件(暂保持 JS // 子组件
import BasicInfoTab from "./BasicInfoTab"; import BasicInfoTab from "./BasicInfoTab";
/** /**
@@ -18,17 +17,13 @@ import BasicInfoTab from "./BasicInfoTab";
* *
* 懒加载策略: * 懒加载策略:
* - BasicInfoTab 内部根据 Tab 切换懒加载数据 * - BasicInfoTab 内部根据 Tab 切换懒加载数据
* - 各 Panel 组件自行获取所需数据(如 BusinessInfoPanel 调用 useBasicInfo
*/ */
const CompanyOverview: React.FC<CompanyOverviewProps> = ({ stockCode }) => { const CompanyOverview: React.FC<CompanyOverviewProps> = ({ stockCode }) => {
const { basicInfo } = useBasicInfo(stockCode);
return ( return (
<VStack spacing={6} align="stretch"> <VStack spacing={6} align="stretch">
{/* 基本信息内容 - 传入 stockCode内部懒加载各 Tab 数据 */} {/* 基本信息内容 - 传入 stockCode内部懒加载各 Tab 数据 */}
<BasicInfoTab <BasicInfoTab stockCode={stockCode} />
stockCode={stockCode}
basicInfo={basicInfo}
/>
</VStack> </VStack>
); );
}; };

View File

@@ -22,6 +22,15 @@ export interface BasicInfo {
email?: string; email?: string;
tel?: string; tel?: string;
company_intro?: string; company_intro?: string;
// 工商信息字段
credit_code?: string;
company_size?: string;
reg_address?: string;
office_address?: string;
accounting_firm?: string;
law_firm?: string;
main_business?: string;
business_scope?: string;
} }
/** /**
@@ -107,23 +116,6 @@ export interface DisclosureSchedule {
disclosure_date?: string; disclosure_date?: string;
} }
/**
* useCompanyOverviewData Hook 返回值
*/
export interface CompanyOverviewData {
basicInfo: BasicInfo | null;
actualControl: ActualControl[];
concentration: Concentration[];
management: Management[];
topCirculationShareholders: Shareholder[];
topShareholders: Shareholder[];
branches: Branch[];
announcements: Announcement[];
disclosureSchedule: DisclosureSchedule[];
loading: boolean;
dataLoaded: boolean;
}
/** /**
* CompanyOverview 组件 Props * CompanyOverview 组件 Props
*/ */

View File

@@ -3,13 +3,11 @@
import React, { useState, useEffect, useCallback, useRef } from "react"; import React, { useState, useEffect, useCallback, useRef } from "react";
import { logger } from "@utils/logger"; import { logger } from "@utils/logger";
import { getApiBase } from "@utils/apiConfig"; import axios from "@utils/axiosConfig";
// 复用原有的展示组件 // 复用原有的展示组件
import DeepAnalysisTab from "../CompanyOverview/DeepAnalysisTab"; import DeepAnalysisTab from "../CompanyOverview/DeepAnalysisTab";
const API_BASE_URL = getApiBase();
/** /**
* Tab 与 API 接口映射 * Tab 与 API 接口映射
* - strategy 和 business 共用 comprehensive 接口 * - strategy 和 business 共用 comprehensive 接口
@@ -84,9 +82,9 @@ const DeepAnalysis = ({ stockCode }) => {
switch (apiKey) { switch (apiKey) {
case "comprehensive": case "comprehensive":
setComprehensiveLoading(true); setComprehensiveLoading(true);
const comprehensiveRes = await fetch( const { data: comprehensiveRes } = await axios.get(
`${API_BASE_URL}/api/company/comprehensive-analysis/${stockCode}` `/api/company/comprehensive-analysis/${stockCode}`
).then((r) => r.json()); );
// 检查 stockCode 是否已变更(防止竞态) // 检查 stockCode 是否已变更(防止竞态)
if (currentStockCodeRef.current === stockCode) { if (currentStockCodeRef.current === stockCode) {
if (comprehensiveRes.success) if (comprehensiveRes.success)
@@ -97,9 +95,9 @@ const DeepAnalysis = ({ stockCode }) => {
case "valueChain": case "valueChain":
setValueChainLoading(true); setValueChainLoading(true);
const valueChainRes = await fetch( const { data: valueChainRes } = await axios.get(
`${API_BASE_URL}/api/company/value-chain-analysis/${stockCode}` `/api/company/value-chain-analysis/${stockCode}`
).then((r) => r.json()); );
if (currentStockCodeRef.current === stockCode) { if (currentStockCodeRef.current === stockCode) {
if (valueChainRes.success) setValueChainData(valueChainRes.data); if (valueChainRes.success) setValueChainData(valueChainRes.data);
loadedApisRef.current.valueChain = true; loadedApisRef.current.valueChain = true;
@@ -108,9 +106,9 @@ const DeepAnalysis = ({ stockCode }) => {
case "keyFactors": case "keyFactors":
setKeyFactorsLoading(true); setKeyFactorsLoading(true);
const keyFactorsRes = await fetch( const { data: keyFactorsRes } = await axios.get(
`${API_BASE_URL}/api/company/key-factors-timeline/${stockCode}` `/api/company/key-factors-timeline/${stockCode}`
).then((r) => r.json()); );
if (currentStockCodeRef.current === stockCode) { if (currentStockCodeRef.current === stockCode) {
if (keyFactorsRes.success) setKeyFactorsData(keyFactorsRes.data); if (keyFactorsRes.success) setKeyFactorsData(keyFactorsRes.data);
loadedApisRef.current.keyFactors = true; loadedApisRef.current.keyFactors = true;
@@ -119,9 +117,9 @@ const DeepAnalysis = ({ stockCode }) => {
case "industryRank": case "industryRank":
setIndustryRankLoading(true); setIndustryRankLoading(true);
const industryRankRes = await fetch( const { data: industryRankRes } = await axios.get(
`${API_BASE_URL}/api/financial/industry-rank/${stockCode}` `/api/financial/industry-rank/${stockCode}`
).then((r) => r.json()); );
if (currentStockCodeRef.current === stockCode) { if (currentStockCodeRef.current === stockCode) {
if (industryRankRes.success) setIndustryRankData(industryRankRes.data); if (industryRankRes.success) setIndustryRankData(industryRankRes.data);
loadedApisRef.current.industryRank = true; loadedApisRef.current.industryRank = true;

View File

@@ -12,9 +12,7 @@ import {
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { Tag } from 'antd'; import { Tag } from 'antd';
import { logger } from '@utils/logger'; import { logger } from '@utils/logger';
import { getApiBase } from '@utils/apiConfig'; import axios from '@utils/axiosConfig';
const API_BASE_URL = getApiBase();
// 黑金主题 // 黑金主题
const THEME = { const THEME = {
@@ -53,10 +51,9 @@ const ForecastPanel = ({ stockCode }) => {
setLoading(true); setLoading(true);
try { try {
const response = await fetch( const { data: result } = await axios.get(
`${API_BASE_URL}/api/stock/${stockCode}/forecast` `/api/stock/${stockCode}/forecast`
); );
const result = await response.json();
if (result.success && result.data) { if (result.success && result.data) {
setForecast(result.data); setForecast(result.data);
} }

View File

@@ -3,11 +3,9 @@
import React, { useState, useEffect, useCallback } from 'react'; import React, { useState, useEffect, useCallback } from 'react';
import { logger } from '@utils/logger'; import { logger } from '@utils/logger';
import { getApiBase } from '@utils/apiConfig'; import axios from '@utils/axiosConfig';
import NewsEventsTab from '../../CompanyOverview/NewsEventsTab'; import NewsEventsTab from '../../CompanyOverview/NewsEventsTab';
const API_BASE_URL = getApiBase();
const NewsPanel = ({ stockCode }) => { const NewsPanel = ({ stockCode }) => {
const [newsEvents, setNewsEvents] = useState([]); const [newsEvents, setNewsEvents] = useState([]);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
@@ -25,10 +23,9 @@ const NewsPanel = ({ stockCode }) => {
// 获取股票名称 // 获取股票名称
const fetchStockName = useCallback(async () => { const fetchStockName = useCallback(async () => {
try { try {
const response = await fetch( const { data: result } = await axios.get(
`${API_BASE_URL}/api/stock/${stockCode}/basic-info` `/api/stock/${stockCode}/basic-info`
); );
const result = await response.json();
if (result.success && result.data) { if (result.success && result.data) {
const name = result.data.SECNAME || result.data.ORGNAME || stockCode; const name = result.data.SECNAME || result.data.ORGNAME || stockCode;
setStockName(name); setStockName(name);
@@ -47,10 +44,9 @@ const NewsPanel = ({ stockCode }) => {
setLoading(true); setLoading(true);
try { try {
const searchTerm = query || stockName || stockCode; const searchTerm = query || stockName || stockCode;
const response = await fetch( const { data: result } = await axios.get(
`${API_BASE_URL}/api/events?q=${encodeURIComponent(searchTerm)}&page=${page}&per_page=10` `/api/events?q=${encodeURIComponent(searchTerm)}&page=${page}&per_page=10`
); );
const result = await response.json();
if (result.success) { if (result.success) {
setNewsEvents(result.data || []); setNewsEvents(result.data || []);

View File

@@ -1,7 +1,7 @@
// src/views/Company/components/MarketDataView/services/marketService.ts // src/views/Company/components/MarketDataView/services/marketService.ts
// MarketDataView API 服务层 // MarketDataView API 服务层
import { getApiBase } from '@utils/apiConfig'; import axios from '@utils/axiosConfig';
import { logger } from '@utils/logger'; import { logger } from '@utils/logger';
import type { import type {
MarketSummary, MarketSummary,
@@ -23,21 +23,13 @@ interface ApiResponse<T> {
message?: string; message?: string;
} }
/**
* API 基础 URL
*/
const getBaseUrl = (): string => getApiBase();
/** /**
* 通用 API 请求函数 * 通用 API 请求函数
*/ */
const apiRequest = async <T>(url: string): Promise<ApiResponse<T>> => { const apiRequest = async <T>(url: string): Promise<ApiResponse<T>> => {
try { try {
const response = await fetch(`${getBaseUrl()}${url}`); const { data } = await axios.get<ApiResponse<T>>(url);
if (!response.ok) { return data;
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
} catch (error) { } catch (error) {
logger.error('marketService', 'apiRequest', error, { url }); logger.error('marketService', 'apiRequest', error, { url });
throw error; throw error;
@@ -80,11 +72,8 @@ export const marketService = {
* @param days 天数,默认 30 天 * @param days 天数,默认 30 天
*/ */
async getBigDealData(stockCode: string, days: number = 30): Promise<BigDealData> { async getBigDealData(stockCode: string, days: number = 30): Promise<BigDealData> {
const response = await fetch(`${getBaseUrl()}/api/market/bigdeal/${stockCode}?days=${days}`); const { data } = await axios.get<BigDealData>(`/api/market/bigdeal/${stockCode}?days=${days}`);
if (!response.ok) { return data;
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
}, },
/** /**
@@ -93,11 +82,8 @@ export const marketService = {
* @param days 天数,默认 30 天 * @param days 天数,默认 30 天
*/ */
async getUnusualData(stockCode: string, days: number = 30): Promise<UnusualData> { async getUnusualData(stockCode: string, days: number = 30): Promise<UnusualData> {
const response = await fetch(`${getBaseUrl()}/api/market/unusual/${stockCode}?days=${days}`); const { data } = await axios.get<UnusualData>(`/api/market/unusual/${stockCode}?days=${days}`);
if (!response.ok) { return data;
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
}, },
/** /**
@@ -132,18 +118,8 @@ export const marketService = {
*/ */
async getMinuteData(stockCode: string): Promise<MinuteData> { async getMinuteData(stockCode: string): Promise<MinuteData> {
try { try {
const response = await fetch(`${getBaseUrl()}/api/stock/${stockCode}/latest-minute`, { const { data } = await axios.get<MinuteData>(`/api/stock/${stockCode}/latest-minute`);
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error('Failed to fetch minute data');
}
const data = await response.json();
if (data.data && Array.isArray(data.data)) { if (data.data && Array.isArray(data.data)) {
return data; return data;
} }

View File

@@ -7,12 +7,10 @@
import { useState, useEffect, useCallback } from 'react'; import { useState, useEffect, useCallback } from 'react';
import { stockService } from '@services/eventService'; import { stockService } from '@services/eventService';
import { logger } from '@utils/logger'; import { logger } from '@utils/logger';
import { getApiBase } from '@utils/apiConfig'; import axios from '@utils/axiosConfig';
import type { StockQuoteCardData } from '../types'; import type { StockQuoteCardData } from '../types';
import type { BasicInfo } from '../../CompanyOverview/types'; import type { BasicInfo } from '../../CompanyOverview/types';
const API_BASE_URL = getApiBase();
/** /**
* 将 API 响应数据转换为 StockQuoteCard 所需格式 * 将 API 响应数据转换为 StockQuoteCard 所需格式
*/ */
@@ -114,8 +112,7 @@ export const useStockQuoteData = (stockCode?: string): UseStockQuoteDataResult =
setBasicLoading(true); setBasicLoading(true);
try { try {
const response = await fetch(`${API_BASE_URL}/api/stock/${stockCode}/basic-info`); const { data: result } = await axios.get(`/api/stock/${stockCode}/basic-info`);
const result = await response.json();
if (result.success) { if (result.success) {
setBasicInfo(result.data); setBasicInfo(result.data);