refactor: 财报披露日程独立为动态跟踪第三个 Tab

- 新建 DisclosureSchedulePanel 组件,独立展示财报披露日程
- 简化 AnnouncementsPanel,移除财报披露日程部分
- DynamicTracking 新增第三个 Tab:财报披露日程
- 更新 mock 数据字段名匹配组件需求

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
zdl
2025-12-10 18:55:30 +08:00
parent c237a4dc0c
commit 2994de98c2
5 changed files with 146 additions and 90 deletions

View File

@@ -11,8 +11,6 @@ import {
Icon,
Card,
CardBody,
SimpleGrid,
Divider,
IconButton,
Button,
Tag,
@@ -25,11 +23,10 @@ import {
ModalFooter,
useDisclosure,
} from "@chakra-ui/react";
import { FaCalendarAlt, FaBullhorn } from "react-icons/fa";
import { FaBullhorn } from "react-icons/fa";
import { ExternalLinkIcon } from "@chakra-ui/icons";
import { useAnnouncementsData } from "../../hooks/useAnnouncementsData";
import { useDisclosureData } from "../../hooks/useDisclosureData";
import { THEME } from "../config";
import { formatDate } from "../utils";
import LoadingState from "./LoadingState";
@@ -39,8 +36,7 @@ interface AnnouncementsPanelProps {
}
const AnnouncementsPanel: React.FC<AnnouncementsPanelProps> = ({ stockCode }) => {
const { announcements, loading: announcementsLoading } = useAnnouncementsData(stockCode);
const { disclosureSchedule, loading: disclosureLoading } = useDisclosureData(stockCode);
const { announcements, loading } = useAnnouncementsData(stockCode);
const { isOpen, onOpen, onClose } = useDisclosure();
const [selectedAnnouncement, setSelectedAnnouncement] = useState<any>(null);
@@ -50,8 +46,6 @@ const AnnouncementsPanel: React.FC<AnnouncementsPanelProps> = ({ stockCode }) =>
onOpen();
};
const loading = announcementsLoading || disclosureLoading;
if (loading) {
return <LoadingState message="加载公告数据..." />;
}
@@ -59,47 +53,6 @@ const AnnouncementsPanel: React.FC<AnnouncementsPanelProps> = ({ stockCode }) =>
return (
<>
<VStack spacing={4} align="stretch">
{/* 财报披露日程 */}
{disclosureSchedule.length > 0 && (
<Box>
<HStack mb={3}>
<Icon as={FaCalendarAlt} color={THEME.gold} />
<Text fontWeight="bold" color={THEME.textPrimary}></Text>
</HStack>
<SimpleGrid columns={{ base: 2, md: 4 }} spacing={3}>
{disclosureSchedule.slice(0, 4).map((schedule: any, idx: number) => (
<Card
key={idx}
bg={schedule.is_disclosed ? "green.900" : "orange.900"}
border="1px solid"
borderColor={schedule.is_disclosed ? "green.600" : "orange.600"}
size="sm"
>
<CardBody p={3}>
<VStack spacing={1}>
<Badge colorScheme={schedule.is_disclosed ? "green" : "orange"}>
{schedule.report_name}
</Badge>
<Text fontSize="sm" fontWeight="bold" color={THEME.textPrimary}>
{schedule.is_disclosed ? "已披露" : "预计"}
</Text>
<Text fontSize="xs" color={THEME.textSecondary}>
{formatDate(
schedule.is_disclosed
? schedule.actual_date
: schedule.latest_scheduled_date
)}
</Text>
</VStack>
</CardBody>
</Card>
))}
</SimpleGrid>
</Box>
)}
<Divider borderColor={THEME.border} />
{/* 最新公告 */}
<Box>
<HStack mb={3}>

View File

@@ -0,0 +1,83 @@
// src/views/Company/components/CompanyOverview/BasicInfoTab/components/DisclosureSchedulePanel.tsx
// 财报披露日程 Tab Panel
import React from "react";
import {
Box,
VStack,
HStack,
Text,
Badge,
Icon,
Card,
CardBody,
SimpleGrid,
} from "@chakra-ui/react";
import { FaCalendarAlt } from "react-icons/fa";
import { useDisclosureData } from "../../hooks/useDisclosureData";
import { THEME } from "../config";
import { formatDate } from "../utils";
import LoadingState from "./LoadingState";
interface DisclosureSchedulePanelProps {
stockCode: string;
}
const DisclosureSchedulePanel: React.FC<DisclosureSchedulePanelProps> = ({ stockCode }) => {
const { disclosureSchedule, loading } = useDisclosureData(stockCode);
if (loading) {
return <LoadingState message="加载披露日程..." />;
}
if (disclosureSchedule.length === 0) {
return (
<Box textAlign="center" py={8}>
<Text color={THEME.textSecondary}></Text>
</Box>
);
}
return (
<VStack spacing={4} align="stretch">
<Box>
<HStack mb={3}>
<Icon as={FaCalendarAlt} color={THEME.gold} />
<Text fontWeight="bold" color={THEME.textPrimary}></Text>
</HStack>
<SimpleGrid columns={{ base: 2, md: 4 }} spacing={3}>
{disclosureSchedule.map((schedule: any, idx: number) => (
<Card
key={idx}
bg={schedule.is_disclosed ? "green.900" : "orange.900"}
border="1px solid"
borderColor={schedule.is_disclosed ? "green.600" : "orange.600"}
size="sm"
>
<CardBody p={3}>
<VStack spacing={1}>
<Badge colorScheme={schedule.is_disclosed ? "green" : "orange"}>
{schedule.report_name}
</Badge>
<Text fontSize="sm" fontWeight="bold" color={THEME.textPrimary}>
{schedule.is_disclosed ? "已披露" : "预计"}
</Text>
<Text fontSize="xs" color={THEME.textSecondary}>
{formatDate(
schedule.is_disclosed
? schedule.actual_date
: schedule.latest_scheduled_date
)}
</Text>
</VStack>
</CardBody>
</Card>
))}
</SimpleGrid>
</Box>
</VStack>
);
};
export default DisclosureSchedulePanel;

View File

@@ -5,7 +5,6 @@ import { IconType } from "react-icons";
import {
FaShareAlt,
FaUserTie,
FaBullhorn,
FaSitemap,
FaInfoCircle,
} from "react-icons/fa";
@@ -72,12 +71,6 @@ export const TAB_CONFIG: TabConfig[] = [
icon: FaUserTie,
enabled: true,
},
{
key: "announcements",
name: "公司公告",
icon: FaBullhorn,
enabled: true,
},
{
key: "branches",
name: "分支机构",

View File

@@ -10,11 +10,14 @@ import {
Tab,
TabPanel,
} from "@chakra-ui/react";
import { FaNewspaper } from "react-icons/fa";
import { FaNewspaper, FaBullhorn, FaCalendarAlt } from "react-icons/fa";
import { logger } from "@utils/logger";
import { getApiBase } from "@utils/apiConfig";
import NewsEventsTab from "../CompanyOverview/NewsEventsTab";
import AnnouncementsPanel from "../CompanyOverview/BasicInfoTab/components/AnnouncementsPanel";
import DisclosureSchedulePanel from "../CompanyOverview/BasicInfoTab/components/DisclosureSchedulePanel";
import { THEME } from "../CompanyOverview/BasicInfoTab/config";
// API配置
const API_BASE_URL = getApiBase();
@@ -22,7 +25,8 @@ const API_BASE_URL = getApiBase();
// 二级 Tab 配置
const TRACKING_TABS = [
{ key: "news", name: "新闻动态", icon: FaNewspaper },
// 后续可扩展更多二级 Tab
{ key: "announcements", name: "公司公告", icon: FaBullhorn },
{ key: "disclosure", name: "财报披露日程", icon: FaCalendarAlt },
];
/**
@@ -144,16 +148,26 @@ const DynamicTracking = ({ stockCode: propStockCode }) => {
};
return (
<Box>
<Box bg={THEME.bg} p={4} borderRadius="md">
<Tabs
variant="enclosed"
colorScheme="blue"
variant="soft-rounded"
index={activeTab}
onChange={setActiveTab}
isLazy
>
<TabList>
<TabList bg={THEME.cardBg} borderBottom="1px solid" borderColor={THEME.border}>
{TRACKING_TABS.map((tab) => (
<Tab key={tab.key} fontWeight="medium">
<Tab
key={tab.key}
fontWeight="medium"
color={THEME.textSecondary}
_selected={{
color: THEME.tabSelected.color,
bg: THEME.tabSelected.bg,
borderRadius: "md",
}}
_hover={{ color: THEME.gold }}
>
{tab.name}
</Tab>
))}
@@ -174,7 +188,15 @@ const DynamicTracking = ({ stockCode: propStockCode }) => {
/>
</TabPanel>
{/* 后续可扩展更多 Tab Panel */}
{/* 公司公告 Tab */}
<TabPanel p={4}>
<AnnouncementsPanel stockCode={stockCode} />
</TabPanel>
{/* 财报披露日程 Tab */}
<TabPanel p={4}>
<DisclosureSchedulePanel stockCode={stockCode} />
</TabPanel>
</TabPanels>
</Tabs>
</Box>