style(DynamicTracking): 应用黑金主题
- NewsEventsTab: 添加黑金主题配色系统 - ForecastPanel: 业绩预告面板黑金样式 - NewsPanel: 切换 blackGold 主题 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -36,6 +36,58 @@ import {
|
||||
FaChevronRight,
|
||||
} from "react-icons/fa";
|
||||
|
||||
// 黑金主题配色
|
||||
const THEME_PRESETS = {
|
||||
blackGold: {
|
||||
bg: "#0A0E17",
|
||||
cardBg: "#1A1F2E",
|
||||
cardHoverBg: "#212633",
|
||||
cardBorder: "rgba(212, 175, 55, 0.2)",
|
||||
cardHoverBorder: "#D4AF37",
|
||||
textPrimary: "#E8E9ED",
|
||||
textSecondary: "#A0A4B8",
|
||||
textMuted: "#6B7280",
|
||||
gold: "#D4AF37",
|
||||
goldLight: "#FFD54F",
|
||||
inputBg: "#151922",
|
||||
inputBorder: "#2D3748",
|
||||
buttonBg: "#D4AF37",
|
||||
buttonText: "#0A0E17",
|
||||
buttonHoverBg: "#FFD54F",
|
||||
badgeS: { bg: "rgba(255, 195, 0, 0.2)", color: "#FFD54F" },
|
||||
badgeA: { bg: "rgba(249, 115, 22, 0.2)", color: "#FB923C" },
|
||||
badgeB: { bg: "rgba(59, 130, 246, 0.2)", color: "#60A5FA" },
|
||||
badgeC: { bg: "rgba(107, 114, 128, 0.2)", color: "#9CA3AF" },
|
||||
tagBg: "rgba(212, 175, 55, 0.15)",
|
||||
tagColor: "#D4AF37",
|
||||
spinnerColor: "#D4AF37",
|
||||
},
|
||||
default: {
|
||||
bg: "white",
|
||||
cardBg: "white",
|
||||
cardHoverBg: "gray.50",
|
||||
cardBorder: "gray.200",
|
||||
cardHoverBorder: "blue.300",
|
||||
textPrimary: "gray.800",
|
||||
textSecondary: "gray.600",
|
||||
textMuted: "gray.500",
|
||||
gold: "blue.500",
|
||||
goldLight: "blue.400",
|
||||
inputBg: "white",
|
||||
inputBorder: "gray.200",
|
||||
buttonBg: "blue.500",
|
||||
buttonText: "white",
|
||||
buttonHoverBg: "blue.600",
|
||||
badgeS: { bg: "red.100", color: "red.600" },
|
||||
badgeA: { bg: "orange.100", color: "orange.600" },
|
||||
badgeB: { bg: "yellow.100", color: "yellow.600" },
|
||||
badgeC: { bg: "green.100", color: "green.600" },
|
||||
tagBg: "cyan.50",
|
||||
tagColor: "cyan.600",
|
||||
spinnerColor: "blue.500",
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* 新闻动态 Tab 组件
|
||||
*
|
||||
@@ -48,6 +100,7 @@ import {
|
||||
* - onSearch: 搜索提交回调 () => void
|
||||
* - onPageChange: 分页回调 (page) => void
|
||||
* - cardBg: 卡片背景色
|
||||
* - themePreset: 主题预设 'blackGold' | 'default'
|
||||
*/
|
||||
const NewsEventsTab = ({
|
||||
newsEvents = [],
|
||||
@@ -65,7 +118,11 @@ const NewsEventsTab = ({
|
||||
onSearch,
|
||||
onPageChange,
|
||||
cardBg,
|
||||
themePreset = "default",
|
||||
}) => {
|
||||
// 获取主题配色
|
||||
const theme = THEME_PRESETS[themePreset] || THEME_PRESETS.default;
|
||||
const isBlackGold = themePreset === "blackGold";
|
||||
// 事件类型图标映射
|
||||
const getEventTypeIcon = (eventType) => {
|
||||
const iconMap = {
|
||||
@@ -80,15 +137,25 @@ const NewsEventsTab = ({
|
||||
return iconMap[eventType] || FaNewspaper;
|
||||
};
|
||||
|
||||
// 重要性颜色映射
|
||||
const getImportanceColor = (importance) => {
|
||||
// 重要性颜色映射 - 根据主题返回不同配色
|
||||
const getImportanceBadgeStyle = (importance) => {
|
||||
if (isBlackGold) {
|
||||
const styles = {
|
||||
S: theme.badgeS,
|
||||
A: theme.badgeA,
|
||||
B: theme.badgeB,
|
||||
C: theme.badgeC,
|
||||
};
|
||||
return styles[importance] || { bg: "rgba(107, 114, 128, 0.2)", color: "#9CA3AF" };
|
||||
}
|
||||
// 默认主题使用 colorScheme
|
||||
const colorMap = {
|
||||
S: "red",
|
||||
A: "orange",
|
||||
B: "yellow",
|
||||
C: "green",
|
||||
};
|
||||
return colorMap[importance] || "gray";
|
||||
return { colorScheme: colorMap[importance] || "gray" };
|
||||
};
|
||||
|
||||
// 处理搜索输入
|
||||
@@ -129,19 +196,26 @@ const NewsEventsTab = ({
|
||||
// 如果开始页大于1,显示省略号
|
||||
if (startPage > 1) {
|
||||
pageButtons.push(
|
||||
<Text key="start-ellipsis" fontSize="sm" color="gray.400">
|
||||
<Text key="start-ellipsis" fontSize="sm" color={theme.textMuted}>
|
||||
...
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
for (let i = startPage; i <= endPage; i++) {
|
||||
const isActive = i === currentPage;
|
||||
pageButtons.push(
|
||||
<Button
|
||||
key={i}
|
||||
size="sm"
|
||||
variant={i === currentPage ? "solid" : "outline"}
|
||||
colorScheme={i === currentPage ? "blue" : "gray"}
|
||||
bg={isActive ? theme.buttonBg : (isBlackGold ? theme.inputBg : undefined)}
|
||||
color={isActive ? theme.buttonText : theme.textSecondary}
|
||||
borderColor={isActive ? theme.gold : theme.cardBorder}
|
||||
borderWidth="1px"
|
||||
_hover={{
|
||||
bg: isActive ? theme.buttonHoverBg : theme.cardHoverBg,
|
||||
borderColor: theme.gold
|
||||
}}
|
||||
onClick={() => handlePageChange(i)}
|
||||
isDisabled={newsLoading}
|
||||
>
|
||||
@@ -153,7 +227,7 @@ const NewsEventsTab = ({
|
||||
// 如果结束页小于总页数,显示省略号
|
||||
if (endPage < totalPages) {
|
||||
pageButtons.push(
|
||||
<Text key="end-ellipsis" fontSize="sm" color="gray.400">
|
||||
<Text key="end-ellipsis" fontSize="sm" color={theme.textMuted}>
|
||||
...
|
||||
</Text>
|
||||
);
|
||||
@@ -164,7 +238,7 @@ const NewsEventsTab = ({
|
||||
|
||||
return (
|
||||
<VStack spacing={4} align="stretch">
|
||||
<Card bg={cardBg} shadow="md">
|
||||
<Card bg={cardBg || theme.cardBg} shadow="md" borderColor={theme.cardBorder} borderWidth={isBlackGold ? "1px" : "0"}>
|
||||
<CardBody>
|
||||
<VStack spacing={4} align="stretch">
|
||||
{/* 搜索框和统计信息 */}
|
||||
@@ -172,17 +246,25 @@ const NewsEventsTab = ({
|
||||
<HStack flex={1} minW="300px">
|
||||
<InputGroup>
|
||||
<InputLeftElement pointerEvents="none">
|
||||
<SearchIcon color="gray.400" />
|
||||
<SearchIcon color={theme.textMuted} />
|
||||
</InputLeftElement>
|
||||
<Input
|
||||
placeholder="搜索相关新闻..."
|
||||
value={searchQuery}
|
||||
onChange={handleInputChange}
|
||||
onKeyPress={handleKeyPress}
|
||||
bg={theme.inputBg}
|
||||
borderColor={theme.inputBorder}
|
||||
color={theme.textPrimary}
|
||||
_placeholder={{ color: theme.textMuted }}
|
||||
_hover={{ borderColor: theme.gold }}
|
||||
_focus={{ borderColor: theme.gold, boxShadow: `0 0 0 1px ${theme.gold}` }}
|
||||
/>
|
||||
</InputGroup>
|
||||
<Button
|
||||
colorScheme="blue"
|
||||
bg={theme.buttonBg}
|
||||
color={theme.buttonText}
|
||||
_hover={{ bg: theme.buttonHoverBg }}
|
||||
onClick={handleSearchSubmit}
|
||||
isLoading={newsLoading}
|
||||
minW="80px"
|
||||
@@ -193,10 +275,10 @@ const NewsEventsTab = ({
|
||||
|
||||
{newsPagination.total > 0 && (
|
||||
<HStack spacing={2}>
|
||||
<Icon as={FaNewspaper} color="blue.500" />
|
||||
<Text fontSize="sm" color="gray.600">
|
||||
<Icon as={FaNewspaper} color={theme.gold} />
|
||||
<Text fontSize="sm" color={theme.textSecondary}>
|
||||
共找到{" "}
|
||||
<Text as="span" fontWeight="bold" color="blue.600">
|
||||
<Text as="span" fontWeight="bold" color={theme.gold}>
|
||||
{newsPagination.total}
|
||||
</Text>{" "}
|
||||
条新闻
|
||||
@@ -211,15 +293,15 @@ const NewsEventsTab = ({
|
||||
{newsLoading ? (
|
||||
<Center h="400px">
|
||||
<VStack spacing={3}>
|
||||
<Spinner size="xl" color="blue.500" thickness="4px" />
|
||||
<Text color="gray.600">正在加载新闻...</Text>
|
||||
<Spinner size="xl" color={theme.spinnerColor} thickness="4px" />
|
||||
<Text color={theme.textSecondary}>正在加载新闻...</Text>
|
||||
</VStack>
|
||||
</Center>
|
||||
) : newsEvents.length > 0 ? (
|
||||
<>
|
||||
<VStack spacing={3} align="stretch">
|
||||
{newsEvents.map((event, idx) => {
|
||||
const importanceColor = getImportanceColor(
|
||||
const importanceBadgeStyle = getImportanceBadgeStyle(
|
||||
event.importance
|
||||
);
|
||||
const eventTypeIcon = getEventTypeIcon(event.event_type);
|
||||
@@ -228,10 +310,12 @@ const NewsEventsTab = ({
|
||||
<Card
|
||||
key={event.id || idx}
|
||||
variant="outline"
|
||||
bg={theme.cardBg}
|
||||
borderColor={theme.cardBorder}
|
||||
_hover={{
|
||||
bg: "gray.50",
|
||||
bg: theme.cardHoverBg,
|
||||
shadow: "md",
|
||||
borderColor: "blue.300",
|
||||
borderColor: theme.cardHoverBorder,
|
||||
}}
|
||||
transition="all 0.2s"
|
||||
>
|
||||
@@ -243,13 +327,14 @@ const NewsEventsTab = ({
|
||||
<HStack>
|
||||
<Icon
|
||||
as={eventTypeIcon}
|
||||
color="blue.500"
|
||||
color={theme.gold}
|
||||
boxSize={5}
|
||||
/>
|
||||
<Text
|
||||
fontWeight="bold"
|
||||
fontSize="lg"
|
||||
lineHeight="1.3"
|
||||
color={theme.textPrimary}
|
||||
>
|
||||
{event.title}
|
||||
</Text>
|
||||
@@ -259,22 +344,29 @@ const NewsEventsTab = ({
|
||||
<HStack spacing={2} flexWrap="wrap">
|
||||
{event.importance && (
|
||||
<Badge
|
||||
colorScheme={importanceColor}
|
||||
variant="solid"
|
||||
{...(isBlackGold ? {} : { colorScheme: importanceBadgeStyle.colorScheme, variant: "solid" })}
|
||||
bg={isBlackGold ? importanceBadgeStyle.bg : undefined}
|
||||
color={isBlackGold ? importanceBadgeStyle.color : undefined}
|
||||
px={2}
|
||||
>
|
||||
{event.importance}级
|
||||
</Badge>
|
||||
)}
|
||||
{event.event_type && (
|
||||
<Badge colorScheme="blue" variant="outline">
|
||||
<Badge
|
||||
{...(isBlackGold ? {} : { colorScheme: "blue", variant: "outline" })}
|
||||
bg={isBlackGold ? "rgba(59, 130, 246, 0.2)" : undefined}
|
||||
color={isBlackGold ? "#60A5FA" : undefined}
|
||||
borderColor={isBlackGold ? "rgba(59, 130, 246, 0.3)" : undefined}
|
||||
>
|
||||
{event.event_type}
|
||||
</Badge>
|
||||
)}
|
||||
{event.invest_score && (
|
||||
<Badge
|
||||
colorScheme="purple"
|
||||
variant="subtle"
|
||||
{...(isBlackGold ? {} : { colorScheme: "purple", variant: "subtle" })}
|
||||
bg={isBlackGold ? "rgba(139, 92, 246, 0.2)" : undefined}
|
||||
color={isBlackGold ? "#A78BFA" : undefined}
|
||||
>
|
||||
投资分: {event.invest_score}
|
||||
</Badge>
|
||||
@@ -287,8 +379,9 @@ const NewsEventsTab = ({
|
||||
<Tag
|
||||
key={kidx}
|
||||
size="sm"
|
||||
colorScheme="cyan"
|
||||
variant="subtle"
|
||||
{...(isBlackGold ? {} : { colorScheme: "cyan", variant: "subtle" })}
|
||||
bg={isBlackGold ? theme.tagBg : undefined}
|
||||
color={isBlackGold ? theme.tagColor : undefined}
|
||||
>
|
||||
{typeof keyword === "string"
|
||||
? keyword
|
||||
@@ -304,7 +397,7 @@ const NewsEventsTab = ({
|
||||
|
||||
{/* 右侧信息栏 */}
|
||||
<VStack align="end" spacing={1} minW="100px">
|
||||
<Text fontSize="xs" color="gray.500">
|
||||
<Text fontSize="xs" color={theme.textMuted}>
|
||||
{event.created_at
|
||||
? new Date(
|
||||
event.created_at
|
||||
@@ -321,9 +414,9 @@ const NewsEventsTab = ({
|
||||
<Icon
|
||||
as={FaEye}
|
||||
boxSize={3}
|
||||
color="gray.400"
|
||||
color={theme.textMuted}
|
||||
/>
|
||||
<Text fontSize="xs" color="gray.500">
|
||||
<Text fontSize="xs" color={theme.textMuted}>
|
||||
{event.view_count}
|
||||
</Text>
|
||||
</HStack>
|
||||
@@ -333,16 +426,16 @@ const NewsEventsTab = ({
|
||||
<Icon
|
||||
as={FaFire}
|
||||
boxSize={3}
|
||||
color="orange.400"
|
||||
color={theme.goldLight}
|
||||
/>
|
||||
<Text fontSize="xs" color="gray.500">
|
||||
<Text fontSize="xs" color={theme.textMuted}>
|
||||
{event.hot_score.toFixed(1)}
|
||||
</Text>
|
||||
</HStack>
|
||||
)}
|
||||
</HStack>
|
||||
{event.creator && (
|
||||
<Text fontSize="xs" color="gray.400">
|
||||
<Text fontSize="xs" color={theme.textMuted}>
|
||||
@{event.creator.username}
|
||||
</Text>
|
||||
)}
|
||||
@@ -353,7 +446,7 @@ const NewsEventsTab = ({
|
||||
{event.description && (
|
||||
<Text
|
||||
fontSize="sm"
|
||||
color="gray.700"
|
||||
color={theme.textSecondary}
|
||||
lineHeight="1.6"
|
||||
>
|
||||
{event.description}
|
||||
@@ -367,18 +460,18 @@ const NewsEventsTab = ({
|
||||
<Box
|
||||
pt={2}
|
||||
borderTop="1px"
|
||||
borderColor="gray.200"
|
||||
borderColor={theme.cardBorder}
|
||||
>
|
||||
<HStack spacing={6} flexWrap="wrap">
|
||||
<HStack spacing={1}>
|
||||
<Icon
|
||||
as={FaChartLine}
|
||||
boxSize={3}
|
||||
color="gray.500"
|
||||
color={theme.textMuted}
|
||||
/>
|
||||
<Text
|
||||
fontSize="xs"
|
||||
color="gray.500"
|
||||
color={theme.textMuted}
|
||||
fontWeight="medium"
|
||||
>
|
||||
相关涨跌:
|
||||
@@ -387,7 +480,7 @@ const NewsEventsTab = ({
|
||||
{event.related_avg_chg !== null &&
|
||||
event.related_avg_chg !== undefined && (
|
||||
<HStack spacing={1}>
|
||||
<Text fontSize="xs" color="gray.500">
|
||||
<Text fontSize="xs" color={theme.textMuted}>
|
||||
平均
|
||||
</Text>
|
||||
<Text
|
||||
@@ -395,8 +488,8 @@ const NewsEventsTab = ({
|
||||
fontWeight="bold"
|
||||
color={
|
||||
event.related_avg_chg > 0
|
||||
? "red.500"
|
||||
: "green.500"
|
||||
? "#EF4444"
|
||||
: "#10B981"
|
||||
}
|
||||
>
|
||||
{event.related_avg_chg > 0 ? "+" : ""}
|
||||
@@ -407,7 +500,7 @@ const NewsEventsTab = ({
|
||||
{event.related_max_chg !== null &&
|
||||
event.related_max_chg !== undefined && (
|
||||
<HStack spacing={1}>
|
||||
<Text fontSize="xs" color="gray.500">
|
||||
<Text fontSize="xs" color={theme.textMuted}>
|
||||
最大
|
||||
</Text>
|
||||
<Text
|
||||
@@ -415,8 +508,8 @@ const NewsEventsTab = ({
|
||||
fontWeight="bold"
|
||||
color={
|
||||
event.related_max_chg > 0
|
||||
? "red.500"
|
||||
: "green.500"
|
||||
? "#EF4444"
|
||||
: "#10B981"
|
||||
}
|
||||
>
|
||||
{event.related_max_chg > 0 ? "+" : ""}
|
||||
@@ -427,7 +520,7 @@ const NewsEventsTab = ({
|
||||
{event.related_week_chg !== null &&
|
||||
event.related_week_chg !== undefined && (
|
||||
<HStack spacing={1}>
|
||||
<Text fontSize="xs" color="gray.500">
|
||||
<Text fontSize="xs" color={theme.textMuted}>
|
||||
周
|
||||
</Text>
|
||||
<Text
|
||||
@@ -435,8 +528,8 @@ const NewsEventsTab = ({
|
||||
fontWeight="bold"
|
||||
color={
|
||||
event.related_week_chg > 0
|
||||
? "red.500"
|
||||
: "green.500"
|
||||
? "#EF4444"
|
||||
: "#10B981"
|
||||
}
|
||||
>
|
||||
{event.related_week_chg > 0
|
||||
@@ -465,7 +558,7 @@ const NewsEventsTab = ({
|
||||
flexWrap="wrap"
|
||||
>
|
||||
{/* 分页信息 */}
|
||||
<Text fontSize="sm" color="gray.600">
|
||||
<Text fontSize="sm" color={theme.textSecondary}>
|
||||
第 {newsPagination.page} / {newsPagination.pages} 页
|
||||
</Text>
|
||||
|
||||
@@ -473,6 +566,11 @@ const NewsEventsTab = ({
|
||||
<HStack spacing={2}>
|
||||
<Button
|
||||
size="sm"
|
||||
bg={isBlackGold ? theme.inputBg : undefined}
|
||||
color={theme.textSecondary}
|
||||
borderColor={theme.cardBorder}
|
||||
borderWidth="1px"
|
||||
_hover={{ bg: theme.cardHoverBg, borderColor: theme.gold }}
|
||||
onClick={() => handlePageChange(1)}
|
||||
isDisabled={!newsPagination.has_prev || newsLoading}
|
||||
leftIcon={<Icon as={FaChevronLeft} />}
|
||||
@@ -481,6 +579,11 @@ const NewsEventsTab = ({
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
bg={isBlackGold ? theme.inputBg : undefined}
|
||||
color={theme.textSecondary}
|
||||
borderColor={theme.cardBorder}
|
||||
borderWidth="1px"
|
||||
_hover={{ bg: theme.cardHoverBg, borderColor: theme.gold }}
|
||||
onClick={() =>
|
||||
handlePageChange(newsPagination.page - 1)
|
||||
}
|
||||
@@ -494,6 +597,11 @@ const NewsEventsTab = ({
|
||||
|
||||
<Button
|
||||
size="sm"
|
||||
bg={isBlackGold ? theme.inputBg : undefined}
|
||||
color={theme.textSecondary}
|
||||
borderColor={theme.cardBorder}
|
||||
borderWidth="1px"
|
||||
_hover={{ bg: theme.cardHoverBg, borderColor: theme.gold }}
|
||||
onClick={() =>
|
||||
handlePageChange(newsPagination.page + 1)
|
||||
}
|
||||
@@ -503,6 +611,11 @@ const NewsEventsTab = ({
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
bg={isBlackGold ? theme.inputBg : undefined}
|
||||
color={theme.textSecondary}
|
||||
borderColor={theme.cardBorder}
|
||||
borderWidth="1px"
|
||||
_hover={{ bg: theme.cardHoverBg, borderColor: theme.gold }}
|
||||
onClick={() => handlePageChange(newsPagination.pages)}
|
||||
isDisabled={!newsPagination.has_next || newsLoading}
|
||||
rightIcon={<Icon as={FaChevronRight} />}
|
||||
@@ -517,11 +630,11 @@ const NewsEventsTab = ({
|
||||
) : (
|
||||
<Center h="400px">
|
||||
<VStack spacing={3}>
|
||||
<Icon as={FaNewspaper} boxSize={16} color="gray.300" />
|
||||
<Text color="gray.500" fontSize="lg" fontWeight="medium">
|
||||
<Icon as={FaNewspaper} boxSize={16} color={isBlackGold ? theme.gold : "gray.300"} opacity={0.5} />
|
||||
<Text color={theme.textSecondary} fontSize="lg" fontWeight="medium">
|
||||
暂无相关新闻
|
||||
</Text>
|
||||
<Text fontSize="sm" color="gray.400">
|
||||
<Text fontSize="sm" color={theme.textMuted}>
|
||||
{searchQuery ? "尝试修改搜索关键词" : "该公司暂无新闻动态"}
|
||||
</Text>
|
||||
</VStack>
|
||||
|
||||
@@ -1,23 +1,49 @@
|
||||
// src/views/Company/components/DynamicTracking/components/ForecastPanel.js
|
||||
// 业绩预告面板
|
||||
// 业绩预告面板 - 黑金主题
|
||||
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import {
|
||||
VStack,
|
||||
Card,
|
||||
CardBody,
|
||||
HStack,
|
||||
Badge,
|
||||
Box,
|
||||
Flex,
|
||||
Text,
|
||||
Spinner,
|
||||
Center,
|
||||
} from '@chakra-ui/react';
|
||||
import { Tag } from 'antd';
|
||||
import { logger } from '@utils/logger';
|
||||
import { getApiBase } from '@utils/apiConfig';
|
||||
import { THEME } from '../../CompanyOverview/BasicInfoTab/config';
|
||||
|
||||
const API_BASE_URL = getApiBase();
|
||||
|
||||
// 黑金主题
|
||||
const THEME = {
|
||||
gold: '#D4AF37',
|
||||
goldLight: 'rgba(212, 175, 55, 0.15)',
|
||||
goldBorder: 'rgba(212, 175, 55, 0.3)',
|
||||
bgDark: '#1A202C',
|
||||
cardBg: 'rgba(26, 32, 44, 0.6)',
|
||||
text: '#E2E8F0',
|
||||
textSecondary: '#A0AEC0',
|
||||
positive: '#E53E3E',
|
||||
negative: '#48BB78',
|
||||
};
|
||||
|
||||
// 预告类型配色
|
||||
const getForecastTypeStyle = (type) => {
|
||||
const styles = {
|
||||
'预增': { color: '#E53E3E', bg: 'rgba(229, 62, 62, 0.15)', border: 'rgba(229, 62, 62, 0.3)' },
|
||||
'预减': { color: '#48BB78', bg: 'rgba(72, 187, 120, 0.15)', border: 'rgba(72, 187, 120, 0.3)' },
|
||||
'扭亏': { color: '#D4AF37', bg: 'rgba(212, 175, 55, 0.15)', border: 'rgba(212, 175, 55, 0.3)' },
|
||||
'首亏': { color: '#48BB78', bg: 'rgba(72, 187, 120, 0.15)', border: 'rgba(72, 187, 120, 0.3)' },
|
||||
'续亏': { color: '#718096', bg: 'rgba(113, 128, 150, 0.15)', border: 'rgba(113, 128, 150, 0.3)' },
|
||||
'续盈': { color: '#E53E3E', bg: 'rgba(229, 62, 62, 0.15)', border: 'rgba(229, 62, 62, 0.3)' },
|
||||
'略增': { color: '#ED8936', bg: 'rgba(237, 137, 54, 0.15)', border: 'rgba(237, 137, 54, 0.3)' },
|
||||
'略减': { color: '#38B2AC', bg: 'rgba(56, 178, 172, 0.15)', border: 'rgba(56, 178, 172, 0.3)' },
|
||||
};
|
||||
return styles[type] || { color: THEME.gold, bg: THEME.goldLight, border: THEME.goldBorder };
|
||||
};
|
||||
|
||||
const ForecastPanel = ({ stockCode }) => {
|
||||
const [forecast, setForecast] = useState(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
@@ -63,33 +89,69 @@ const ForecastPanel = ({ stockCode }) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<VStack spacing={4} align="stretch">
|
||||
{forecast.forecasts.map((item, idx) => (
|
||||
<Card key={idx} bg={THEME.cardBg} borderColor={THEME.border} borderWidth="1px">
|
||||
<CardBody>
|
||||
<HStack justify="space-between" mb={2}>
|
||||
<Badge colorScheme="blue">{item.forecast_type}</Badge>
|
||||
<VStack spacing={3} align="stretch">
|
||||
{forecast.forecasts.map((item, idx) => {
|
||||
const typeStyle = getForecastTypeStyle(item.forecast_type);
|
||||
|
||||
return (
|
||||
<Box
|
||||
key={idx}
|
||||
bg={THEME.cardBg}
|
||||
border="1px solid"
|
||||
borderColor={THEME.goldBorder}
|
||||
borderRadius="md"
|
||||
p={4}
|
||||
>
|
||||
{/* 头部:类型标签 + 报告期 */}
|
||||
<Flex justify="space-between" align="center" mb={3}>
|
||||
<Tag
|
||||
style={{
|
||||
color: typeStyle.color,
|
||||
background: typeStyle.bg,
|
||||
border: `1px solid ${typeStyle.border}`,
|
||||
fontWeight: 500,
|
||||
}}
|
||||
>
|
||||
{item.forecast_type}
|
||||
</Tag>
|
||||
<Text fontSize="sm" color={THEME.textSecondary}>
|
||||
报告期: {item.report_date}
|
||||
</Text>
|
||||
</HStack>
|
||||
<Text mb={2} color={THEME.text}>{item.content}</Text>
|
||||
</Flex>
|
||||
|
||||
{/* 内容 */}
|
||||
<Text color={THEME.text} fontSize="sm" lineHeight="1.6" mb={3}>
|
||||
{item.content}
|
||||
</Text>
|
||||
|
||||
{/* 原因(如有) */}
|
||||
{item.reason && (
|
||||
<Text fontSize="sm" color={THEME.textSecondary}>
|
||||
<Text fontSize="xs" color={THEME.textSecondary} mb={3}>
|
||||
{item.reason}
|
||||
</Text>
|
||||
)}
|
||||
{item.change_range?.lower && (
|
||||
<HStack mt={2}>
|
||||
<Text fontSize="sm" color={THEME.textSecondary}>预计变动范围:</Text>
|
||||
<Badge colorScheme="green">
|
||||
|
||||
{/* 变动范围 */}
|
||||
{item.change_range?.lower !== undefined && (
|
||||
<Flex align="center" gap={2}>
|
||||
<Text fontSize="sm" color={THEME.textSecondary}>
|
||||
预计变动范围:
|
||||
</Text>
|
||||
<Tag
|
||||
style={{
|
||||
color: THEME.gold,
|
||||
background: THEME.goldLight,
|
||||
border: `1px solid ${THEME.goldBorder}`,
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
{item.change_range.lower}% ~ {item.change_range.upper}%
|
||||
</Badge>
|
||||
</HStack>
|
||||
</Tag>
|
||||
</Flex>
|
||||
)}
|
||||
</CardBody>
|
||||
</Card>
|
||||
))}
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
</VStack>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -107,7 +107,7 @@ const NewsPanel = ({ stockCode }) => {
|
||||
onSearchChange={handleSearchChange}
|
||||
onSearch={handleSearch}
|
||||
onPageChange={handlePageChange}
|
||||
cardBg="white"
|
||||
themePreset="blackGold"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user