feat: 动态跟踪添加新闻动态二级 Tab

- 添加 Tabs 结构支持二级 Tab 扩展
- Tab1: 新闻动态(复用 NewsEventsTab 组件)
- 实现 loadNewsEvents 数据加载逻辑
- 支持搜索和分页功能
- 自动获取股票名称用于新闻搜索

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
zdl
2025-12-09 18:46:37 +08:00
parent 40f6eaced6
commit 4274341ed5

View File

@@ -1,45 +1,183 @@
// src/views/Company/components/DynamicTracking/index.js
// 动态跟踪 - 独立一级 Tab 组件
// 动态跟踪 - 独立一级 Tab 组件(包含新闻动态等二级 Tab
import React from "react";
import React, { useState, useEffect, useCallback } from "react";
import {
Box,
VStack,
Text,
Icon,
Card,
CardBody,
Tabs,
TabList,
TabPanels,
Tab,
TabPanel,
} from "@chakra-ui/react";
import { FaNewspaper } from "react-icons/fa";
import { logger } from "@utils/logger";
import { getApiBase } from "@utils/apiConfig";
import NewsEventsTab from "../CompanyOverview/NewsEventsTab";
// API配置
const API_BASE_URL = getApiBase();
// 二级 Tab 配置
const TRACKING_TABS = [
{ key: "news", name: "新闻动态", icon: FaNewspaper },
// 后续可扩展更多二级 Tab
];
/**
* 动态跟踪组件
*
* 功能:
* - 预留二级 Tab 结构
* - 后续放入新闻动态等
* - 二级 Tab 结构
* - Tab1: 新闻动态(复用 NewsEventsTab
* - 预留后续扩展
*
* @param {Object} props
* @param {string} props.stockCode - 股票代码
*/
const DynamicTracking = ({ stockCode }) => {
const DynamicTracking = ({ stockCode: propStockCode }) => {
const [stockCode, setStockCode] = useState(propStockCode || "000001");
const [activeTab, setActiveTab] = useState(0);
// 新闻动态状态
const [newsEvents, setNewsEvents] = useState([]);
const [newsLoading, setNewsLoading] = useState(false);
const [newsPagination, setNewsPagination] = useState({
page: 1,
per_page: 10,
total: 0,
pages: 0,
has_next: false,
has_prev: false,
});
const [searchQuery, setSearchQuery] = useState("");
const [stockName, setStockName] = useState("");
const [dataLoaded, setDataLoaded] = useState(false);
// 监听 props 中的 stockCode 变化
useEffect(() => {
if (propStockCode && propStockCode !== stockCode) {
setStockCode(propStockCode);
setDataLoaded(false);
setNewsEvents([]);
setStockName("");
setSearchQuery("");
}
}, [propStockCode, stockCode]);
// 获取股票名称(用于搜索)
const fetchStockName = useCallback(async () => {
try {
const response = await fetch(
`${API_BASE_URL}/api/stock/${stockCode}/basic-info`
);
const result = await response.json();
if (result.success && result.data) {
const name = result.data.SECNAME || result.data.ORGNAME || stockCode;
setStockName(name);
return name;
}
return stockCode;
} catch (err) {
logger.error("DynamicTracking", "fetchStockName", err, { stockCode });
return stockCode;
}
}, [stockCode]);
// 加载新闻事件数据
const loadNewsEvents = useCallback(
async (query, page = 1) => {
setNewsLoading(true);
try {
const searchTerm = query || stockName || stockCode;
const response = await fetch(
`${API_BASE_URL}/api/events?q=${encodeURIComponent(searchTerm)}&page=${page}&per_page=10`
);
const result = await response.json();
if (result.success) {
setNewsEvents(result.data || []);
setNewsPagination({
page: result.pagination?.page || page,
per_page: result.pagination?.per_page || 10,
total: result.pagination?.total || 0,
pages: result.pagination?.pages || 0,
has_next: result.pagination?.has_next || false,
has_prev: result.pagination?.has_prev || false,
});
}
} catch (err) {
logger.error("DynamicTracking", "loadNewsEvents", err, { stockCode });
setNewsEvents([]);
} finally {
setNewsLoading(false);
}
},
[stockCode, stockName]
);
// 首次加载
useEffect(() => {
const initLoad = async () => {
if (stockCode && !dataLoaded) {
const name = await fetchStockName();
await loadNewsEvents(name, 1);
setDataLoaded(true);
}
};
initLoad();
}, [stockCode, dataLoaded, fetchStockName, loadNewsEvents]);
// 搜索处理
const handleSearchChange = (value) => {
setSearchQuery(value);
};
const handleSearch = () => {
loadNewsEvents(searchQuery || stockName, 1);
};
// 分页处理
const handlePageChange = (page) => {
loadNewsEvents(searchQuery || stockName, page);
};
return (
<Card bg="white" shadow="sm">
<CardBody>
<VStack spacing={4} py={12}>
<Icon as={FaNewspaper} boxSize={12} color="gray.300" />
<Text fontSize="lg" color="gray.500">
动态跟踪
</Text>
<Text fontSize="sm" color="gray.400">
后续将添加新闻动态等内容
</Text>
<Text fontSize="xs" color="gray.300">
股票代码: {stockCode}
</Text>
</VStack>
</CardBody>
</Card>
<Box>
<Tabs
variant="enclosed"
colorScheme="blue"
index={activeTab}
onChange={setActiveTab}
>
<TabList>
{TRACKING_TABS.map((tab) => (
<Tab key={tab.key} fontWeight="medium">
{tab.name}
</Tab>
))}
</TabList>
<TabPanels>
{/* 新闻动态 Tab */}
<TabPanel p={4}>
<NewsEventsTab
newsEvents={newsEvents}
newsLoading={newsLoading}
newsPagination={newsPagination}
searchQuery={searchQuery}
onSearchChange={handleSearchChange}
onSearch={handleSearch}
onPageChange={handlePageChange}
cardBg="white"
/>
</TabPanel>
{/* 后续可扩展更多 Tab Panel */}
</TabPanels>
</Tabs>
</Box>
);
};