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:
@@ -180,48 +180,53 @@ export const PINGAN_BANK_DATA = {
|
||||
announcements: [
|
||||
{
|
||||
title: '平安银行股份有限公司2024年第三季度报告',
|
||||
publish_date: '2024-10-28',
|
||||
type: '定期报告',
|
||||
summary: '2024年前三季度实现营业收入1245.6亿元,同比增长8.2%;净利润402.3亿元,同比增长12.5%',
|
||||
announce_date: '2024-10-28',
|
||||
info_type: '定期报告',
|
||||
format: 'PDF',
|
||||
file_size: 2580,
|
||||
url: '/announcement/detail/ann_20241028_001'
|
||||
},
|
||||
{
|
||||
title: '关于召开2024年第一次临时股东大会的通知',
|
||||
publish_date: '2024-10-15',
|
||||
type: '临时公告',
|
||||
summary: '定于2024年11月5日召开2024年第一次临时股东大会,审议关于调整董事会成员等议案',
|
||||
announce_date: '2024-10-15',
|
||||
info_type: '临时公告',
|
||||
format: 'PDF',
|
||||
file_size: 156,
|
||||
url: '/announcement/detail/ann_20241015_001'
|
||||
},
|
||||
{
|
||||
title: '平安银行股份有限公司关于完成注册资本变更登记的公告',
|
||||
publish_date: '2024-09-20',
|
||||
type: '临时公告',
|
||||
summary: '公司已完成注册资本由人民币194.06亿元变更为194.06亿元的工商变更登记手续',
|
||||
announce_date: '2024-09-20',
|
||||
info_type: '临时公告',
|
||||
format: 'PDF',
|
||||
file_size: 89,
|
||||
url: '/announcement/detail/ann_20240920_001'
|
||||
},
|
||||
{
|
||||
title: '平安银行股份有限公司2024年半年度报告',
|
||||
publish_date: '2024-08-28',
|
||||
type: '定期报告',
|
||||
summary: '2024年上半年实现营业收入828.5亿元,同比增长7.8%;净利润265.4亿元,同比增长11.2%',
|
||||
announce_date: '2024-08-28',
|
||||
info_type: '定期报告',
|
||||
format: 'PDF',
|
||||
file_size: 3420,
|
||||
url: '/announcement/detail/ann_20240828_001'
|
||||
},
|
||||
{
|
||||
title: '关于2024年上半年利润分配预案的公告',
|
||||
publish_date: '2024-08-20',
|
||||
type: '分配方案',
|
||||
summary: '拟以总股本194.06亿股为基数,向全体股东每10股派发现金红利2.8元(含税)',
|
||||
announce_date: '2024-08-20',
|
||||
info_type: '分配方案',
|
||||
format: 'PDF',
|
||||
file_size: 245,
|
||||
url: '/announcement/detail/ann_20240820_001'
|
||||
}
|
||||
],
|
||||
|
||||
// 披露时间表
|
||||
disclosureSchedule: [
|
||||
{ report_type: '2024年年度报告', planned_date: '2025-04-30', status: '未披露' },
|
||||
{ report_type: '2024年第四季度报告', planned_date: '2025-01-31', status: '未披露' },
|
||||
{ report_type: '2024年第三季度报告', planned_date: '2024-10-31', status: '已披露' },
|
||||
{ report_type: '2024年半年度报告', planned_date: '2024-08-31', status: '已披露' },
|
||||
{ report_type: '2024年第一季度报告', planned_date: '2024-04-30', status: '已披露' }
|
||||
{ report_name: '2024年年度报告', is_disclosed: false, actual_date: null, latest_scheduled_date: '2025-04-30' },
|
||||
{ report_name: '2024年第四季度报告', is_disclosed: false, actual_date: null, latest_scheduled_date: '2025-01-31' },
|
||||
{ report_name: '2024年第三季度报告', is_disclosed: true, actual_date: '2024-10-28', latest_scheduled_date: '2024-10-31' },
|
||||
{ report_name: '2024年半年度报告', is_disclosed: true, actual_date: '2024-08-28', latest_scheduled_date: '2024-08-31' },
|
||||
{ report_name: '2024年第一季度报告', is_disclosed: true, actual_date: '2024-04-28', latest_scheduled_date: '2024-04-30' }
|
||||
],
|
||||
|
||||
// 综合分析 - 结构与组件期望格式匹配
|
||||
@@ -1064,14 +1069,14 @@ export const generateCompanyData = (stockCode, stockName = '示例公司') => {
|
||||
{ name: '广州分公司', address: '广州市天河区某路789号', phone: '020-12345678', type: '分公司', establish_date: '2014-03-20' },
|
||||
],
|
||||
announcements: [
|
||||
{ title: `${stockName}2024年第三季度报告`, publish_date: '2024-10-28', type: '定期报告', summary: '业绩稳步增长', url: '#' },
|
||||
{ title: `${stockName}2024年半年度报告`, publish_date: '2024-08-28', type: '定期报告', summary: '经营情况良好', url: '#' },
|
||||
{ title: `关于重大合同签订的公告`, publish_date: '2024-07-15', type: '临时公告', summary: '签订重要销售合同', url: '#' },
|
||||
{ title: `${stockName}2024年第三季度报告`, announce_date: '2024-10-28', info_type: '定期报告', format: 'PDF', file_size: 1850, url: '#' },
|
||||
{ title: `${stockName}2024年半年度报告`, announce_date: '2024-08-28', info_type: '定期报告', format: 'PDF', file_size: 2340, url: '#' },
|
||||
{ title: `关于重大合同签订的公告`, announce_date: '2024-07-15', info_type: '临时公告', format: 'PDF', file_size: 128, url: '#' },
|
||||
],
|
||||
disclosureSchedule: [
|
||||
{ report_type: '2024年年度报告', planned_date: '2025-04-30', status: '未披露' },
|
||||
{ report_type: '2024年第三季度报告', planned_date: '2024-10-31', status: '已披露' },
|
||||
{ report_type: '2024年半年度报告', planned_date: '2024-08-31', status: '已披露' },
|
||||
{ report_name: '2024年年度报告', is_disclosed: false, actual_date: null, latest_scheduled_date: '2025-04-30' },
|
||||
{ report_name: '2024年第三季度报告', is_disclosed: true, actual_date: '2024-10-28', latest_scheduled_date: '2024-10-31' },
|
||||
{ report_name: '2024年半年度报告', is_disclosed: true, actual_date: '2024-08-28', latest_scheduled_date: '2024-08-31' },
|
||||
],
|
||||
comprehensiveAnalysis: {
|
||||
qualitative_analysis: {
|
||||
|
||||
@@ -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}>
|
||||
|
||||
@@ -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;
|
||||
@@ -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: "分支机构",
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user