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:
@@ -1,45 +1,183 @@
|
|||||||
// src/views/Company/components/DynamicTracking/index.js
|
// src/views/Company/components/DynamicTracking/index.js
|
||||||
// 动态跟踪 - 独立一级 Tab 组件
|
// 动态跟踪 - 独立一级 Tab 组件(包含新闻动态等二级 Tab)
|
||||||
|
|
||||||
import React from "react";
|
import React, { useState, useEffect, useCallback } from "react";
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
VStack,
|
Tabs,
|
||||||
Text,
|
TabList,
|
||||||
Icon,
|
TabPanels,
|
||||||
Card,
|
Tab,
|
||||||
CardBody,
|
TabPanel,
|
||||||
} from "@chakra-ui/react";
|
} from "@chakra-ui/react";
|
||||||
import { FaNewspaper } from "react-icons/fa";
|
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 {Object} props
|
||||||
* @param {string} props.stockCode - 股票代码
|
* @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 (
|
return (
|
||||||
<Card bg="white" shadow="sm">
|
<Box>
|
||||||
<CardBody>
|
<Tabs
|
||||||
<VStack spacing={4} py={12}>
|
variant="enclosed"
|
||||||
<Icon as={FaNewspaper} boxSize={12} color="gray.300" />
|
colorScheme="blue"
|
||||||
<Text fontSize="lg" color="gray.500">
|
index={activeTab}
|
||||||
动态跟踪
|
onChange={setActiveTab}
|
||||||
</Text>
|
>
|
||||||
<Text fontSize="sm" color="gray.400">
|
<TabList>
|
||||||
后续将添加新闻动态等内容
|
{TRACKING_TABS.map((tab) => (
|
||||||
</Text>
|
<Tab key={tab.key} fontWeight="medium">
|
||||||
<Text fontSize="xs" color="gray.300">
|
{tab.name}
|
||||||
股票代码: {stockCode}
|
</Tab>
|
||||||
</Text>
|
))}
|
||||||
</VStack>
|
</TabList>
|
||||||
</CardBody>
|
|
||||||
</Card>
|
<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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user