更新Company页面的UI为FUI风格

This commit is contained in:
2025-12-18 08:23:04 +08:00
parent 26548c7036
commit 87ddc79252
18 changed files with 224 additions and 68 deletions

View File

@@ -30,22 +30,23 @@ export interface Theme {
}
// 黑金主题配置
// 注:文字颜色使用更亮的金色(#F4D03F以提高对比度
export const THEME: Theme = {
bg: "gray.900",
cardBg: "gray.800",
tableBg: "gray.700",
tableHoverBg: "gray.600",
gold: "#D4AF37",
goldLight: "#F0D78C",
gold: "#F4D03F", // 亮黄金色(用于文字,对比度更好)
goldLight: "#F0D78C", // 浅金色(用于次要文字)
textPrimary: "white",
textSecondary: "gray.400",
border: "rgba(212, 175, 55, 0.3)",
border: "rgba(212, 175, 55, 0.3)", // 边框保持原色
tabSelected: {
bg: "#D4AF37",
bg: "#D4AF37", // 选中背景保持深金色
color: "gray.900",
},
tabUnselected: {
color: "#D4AF37",
color: "#F4D03F", // 未选中使用亮金色
},
};

View File

@@ -11,12 +11,12 @@ import { Box, HStack, VStack, Text, Badge, Tag, TagLabel } from '@chakra-ui/reac
import { formatPercentage, formatBusinessRevenue } from '@utils/priceFormatters';
import type { BusinessTreeItemProps } from '../types';
// 黑金主题配置
// 黑金主题配置(使用更亮的金色提高对比度)
const THEME = {
bg: 'gray.700',
gold: '#D4AF37',
gold: '#F4D03F', // 亮金色
goldLight: '#F0D78C',
textPrimary: '#D4AF37',
textPrimary: '#F4D03F', // 亮金色(提高对比度)
textSecondary: 'gray.400',
border: 'rgba(212, 175, 55, 0.5)',
};

View File

@@ -9,9 +9,9 @@ import React, { memo } from 'react';
import { HStack, VStack, Box, Text, Icon, Badge } from '@chakra-ui/react';
import { FaArrowRight } from 'react-icons/fa';
// 黑金主题配置
// 黑金主题配置(使用更亮的金色提高对比度)
const THEME = {
gold: '#D4AF37',
gold: '#F4D03F',
textSecondary: 'gray.400',
upstream: {
active: 'orange.500',

View File

@@ -13,10 +13,10 @@ import {
Tab,
} from '@chakra-ui/react';
// 黑金主题配置
// 黑金主题配置(使用更亮的金色提高对比度)
const THEME = {
gold: '#D4AF37',
textPrimary: '#D4AF37',
gold: '#F4D03F',
textPrimary: '#F4D03F',
textSecondary: 'gray.400',
inputBg: 'gray.700',
inputBorder: 'gray.600',

View File

@@ -27,9 +27,9 @@ import type { BusinessSegment } from '../types';
const THEME = {
cardBg: 'gray.800',
innerCardBg: 'gray.700',
gold: '#D4AF37',
gold: '#F4D03F',
goldLight: '#F0D78C',
textPrimary: '#D4AF37',
textPrimary: '#F4D03F',
textSecondary: 'gray.400',
border: 'rgba(212, 175, 55, 0.3)',
};

View File

@@ -23,8 +23,8 @@ import type { BusinessStructure } from '../types';
// 黑金主题配置
const THEME = {
cardBg: 'gray.800',
gold: '#D4AF37',
textPrimary: '#D4AF37',
gold: '#F4D03F',
textPrimary: '#F4D03F',
border: 'rgba(212, 175, 55, 0.3)',
};

View File

@@ -15,13 +15,13 @@ import type { IconType } from 'react-icons';
// ==================== 主题常量 ====================
export const THEME = {
// 深色背景区域(核心定位)
// 深色背景区域(核心定位)- 使用更亮的金色提高对比度
dark: {
bg: '#1A202C',
cardBg: '#252D3A',
border: '#C9A961',
borderGradient: 'linear-gradient(90deg, #C9A961, #8B7355)',
titleColor: '#C9A961',
border: '#E8C14D',
borderGradient: 'linear-gradient(90deg, #E8C14D, #A08040)',
titleColor: '#E8C14D',
textColor: '#E2E8F0',
subtextColor: '#A0AEC0',
},
@@ -29,11 +29,11 @@ export const THEME = {
light: {
bg: '#1E2530',
cardBg: '#252D3A',
titleColor: '#C9A961',
titleColor: '#E8C14D',
textColor: '#E2E8F0',
subtextColor: '#A0AEC0',
tagBg: 'rgba(201, 169, 97, 0.15)',
tagColor: '#C9A961',
tagBg: 'rgba(232, 193, 77, 0.15)',
tagColor: '#E8C14D',
},
} as const;

View File

@@ -31,9 +31,9 @@ import type { KeyFactors } from '../types';
const THEME = {
bg: '#1A202C',
cardBg: '#252D3A',
border: '#C9A961',
borderGradient: 'linear-gradient(90deg, #C9A961, #8B7355)',
titleColor: '#C9A961',
border: '#E8C14D',
borderGradient: 'linear-gradient(90deg, #E8C14D, #A08040)',
titleColor: '#E8C14D',
textColor: '#E2E8F0',
subtextColor: '#A0AEC0',
} as const;

View File

@@ -24,9 +24,9 @@ import type { DevelopmentTimeline } from '../types';
const THEME = {
bg: '#1A202C',
cardBg: '#252D3A',
border: '#C9A961',
borderGradient: 'linear-gradient(90deg, #C9A961, #8B7355)',
titleColor: '#C9A961',
border: '#E8C14D',
borderGradient: 'linear-gradient(90deg, #E8C14D, #A08040)',
titleColor: '#E8C14D',
textColor: '#E2E8F0',
subtextColor: '#A0AEC0',
} as const;

View File

@@ -34,9 +34,9 @@ import type { ValueChainData, ValueChainNode } from '../types';
// 黑金主题配置
const THEME = {
cardBg: 'gray.800',
gold: '#D4AF37',
gold: '#F4D03F',
goldLight: '#F0D78C',
textPrimary: '#D4AF37',
textPrimary: '#F4D03F',
textSecondary: 'gray.400',
};

View File

@@ -39,7 +39,7 @@ import type { ValueChainNodeCardProps, RelatedCompany } from '../../types';
// 黑金主题配置
const THEME = {
cardBg: 'gray.700',
gold: '#D4AF37',
gold: '#F4D03F',
goldLight: '#F0D78C',
textPrimary: 'white',
textSecondary: 'gray.400',

View File

@@ -38,22 +38,22 @@ import {
} from "react-icons/fa";
import { getEventDetailUrl } from "@/utils/idEncoder";
// 黑金主题配色
// 黑金主题配色(文字使用更亮的金色提高对比度)
const THEME_PRESETS = {
blackGold: {
bg: "#0A0E17",
cardBg: "#1A1F2E",
cardHoverBg: "#212633",
cardBorder: "rgba(212, 175, 55, 0.2)",
cardHoverBorder: "#D4AF37",
cardHoverBorder: "#F4D03F", // 亮金色
textPrimary: "#E8E9ED",
textSecondary: "#A0A4B8",
textMuted: "#6B7280",
gold: "#D4AF37",
gold: "#F4D03F", // 亮金色(用于文字)
goldLight: "#FFD54F",
inputBg: "#151922",
inputBorder: "#2D3748",
buttonBg: "#D4AF37",
buttonBg: "#D4AF37", // 按钮背景保持深金色
buttonText: "#0A0E17",
buttonHoverBg: "#FFD54F",
badgeS: { bg: "rgba(255, 195, 0, 0.2)", color: "#FFD54F" },
@@ -61,8 +61,8 @@ const THEME_PRESETS = {
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",
tagColor: "#F4D03F", // 亮金色
spinnerColor: "#F4D03F", // 亮金色
},
default: {
bg: "white",

View File

@@ -10,17 +10,18 @@ import type { Shareholder } from "../../types";
import { THEME } from "../../BasicInfoTab/config";
// antd 表格黑金主题配置
// 使用更亮的金色以提高对比度
const TABLE_THEME = {
token: {
colorBgContainer: "#2D3748", // gray.700
colorText: "white",
colorTextHeading: "#D4AF37", // 金色
colorTextHeading: "#F4D03F", // 金色(提高对比度)
colorBorderSecondary: "rgba(212, 175, 55, 0.3)",
},
components: {
Table: {
headerBg: "#1A202C", // gray.900
headerColor: "#D4AF37", // 金色
headerColor: "#F4D03F", // 金色(提高对比度)
rowHoverBg: "rgba(212, 175, 55, 0.15)", // 金色半透明,文字更清晰
borderColor: "rgba(212, 175, 55, 0.2)",
},
@@ -135,7 +136,7 @@ const ShareholdersTable: React.FC<ShareholdersTableProps> = ({
ellipsis: true,
render: (name: string) => (
<Tooltip title={name}>
<span style={{ fontWeight: 500, color: "#D4AF37" }}>{name}</span>
<span style={{ fontWeight: 500, color: "#F4D03F" }}>{name}</span>
</Tooltip>
),
},
@@ -158,7 +159,7 @@ const ShareholdersTable: React.FC<ShareholdersTableProps> = ({
responsive: ["md"],
sorter: (a: Shareholder, b: Shareholder) => (a.holding_shares || 0) - (b.holding_shares || 0),
render: (shares: number) => (
<span style={{ color: "#D4AF37" }}>{formatShares(shares)}</span>
<span style={{ color: "#F4D03F" }}>{formatShares(shares)}</span>
),
},
{

View File

@@ -1,7 +1,7 @@
// src/views/Company/components/MarketDataView/components/panels/TradeDataPanel/KLineModule.tsx
// K线模块 - 日K线/分时图切换展示(黑金主题 + 专业技术指标 + 商品数据叠加)
// K线模块 - 日K线/分时图切换展示(黑金主题 + 专业技术指标 + 商品数据叠加 + 分时盘口
import React, { useState, useMemo, useCallback } from 'react';
import React, { useState, useMemo, useCallback, useEffect } from 'react';
import {
Box,
Text,
@@ -20,11 +20,17 @@ import {
MenuItem,
MenuDivider,
Tooltip,
Grid,
GridItem,
} from '@chakra-ui/react';
import { RepeatIcon, InfoIcon, ChevronDownIcon, ViewIcon, ViewOffIcon } from '@chakra-ui/icons';
import { BarChart2, Clock, TrendingUp, Calendar, LineChart, Activity, Pencil } from 'lucide-react';
import ReactECharts from 'echarts-for-react';
// 导入实时行情 Hook 和五档盘口组件
import { useRealtimeQuote } from '@views/StockOverview/components/FlexScreen/hooks';
import OrderBookPanel from '@views/StockOverview/components/FlexScreen/components/OrderBookPanel';
import { darkGoldTheme, PERIOD_OPTIONS } from '../../../constants';
import {
getKLineDarkGoldOption,
@@ -90,6 +96,7 @@ const KLineModule: React.FC<KLineModuleProps> = ({
onChartClick,
selectedPeriod,
onPeriodChange,
stockCode,
}) => {
const [mode, setMode] = useState<ChartMode>('daily');
const [subIndicator, setSubIndicator] = useState<IndicatorType>('MACD');
@@ -97,8 +104,34 @@ const KLineModule: React.FC<KLineModuleProps> = ({
const [showAnalysis, setShowAnalysis] = useState<boolean>(true);
const [drawingType, setDrawingType] = useState<DrawingType>('NONE');
const [overlayMetrics, setOverlayMetrics] = useState<OverlayMetricData[]>([]);
const [showOrderBook, setShowOrderBook] = useState<boolean>(true);
const hasMinuteData = minuteData && minuteData.data && minuteData.data.length > 0;
// 实时行情数据(用于五档盘口)
const subscribedCodes = useMemo(() => {
if (!stockCode || mode !== 'minute') return [];
return [stockCode];
}, [stockCode, mode]);
const { quotes, connected } = useRealtimeQuote(subscribedCodes);
// 获取当前股票的行情数据
const currentQuote = useMemo(() => {
if (!stockCode) return null;
// 尝试不同的代码格式
return quotes[stockCode] || quotes[`${stockCode}.SH`] || quotes[`${stockCode}.SZ`] || null;
}, [quotes, stockCode]);
// 判断是否在交易时间
const isInTradingHours = useMemo(() => {
const now = new Date();
const hours = now.getHours();
const minutes = now.getMinutes();
const totalMinutes = hours * 60 + minutes;
// 9:15-11:30 或 13:00-15:00
return (totalMinutes >= 555 && totalMinutes <= 690) || (totalMinutes >= 780 && totalMinutes <= 900);
}, []);
// 计算股票数据的日期范围(用于查询商品数据)
const stockDateRange = useMemo(() => {
if (tradeData.length === 0) return undefined;
@@ -369,19 +402,35 @@ const KLineModule: React.FC<KLineModuleProps> = ({
</>
)}
{/* 分时模式下的刷新按钮 */}
{/* 分时模式下的控制按钮 */}
{mode === 'minute' && (
<Button
leftIcon={<RepeatIcon />}
size="sm"
variant="outline"
onClick={onLoadMinuteData}
isLoading={minuteLoading}
loadingText="获取中"
{...inactiveButtonStyle}
>
</Button>
<>
{/* 显示/隐藏盘口 */}
<Tooltip label={showOrderBook ? '隐藏盘口' : '显示盘口'} placement="top" hasArrow>
<Button
size="sm"
variant="outline"
onClick={() => setShowOrderBook(!showOrderBook)}
{...(showOrderBook ? activeButtonStyle : inactiveButtonStyle)}
minW="80px"
>
{showOrderBook ? '隐藏盘口' : '显示盘口'}
</Button>
</Tooltip>
{/* 刷新按钮 */}
<Button
leftIcon={<RepeatIcon />}
size="sm"
variant="outline"
onClick={onLoadMinuteData}
isLoading={minuteLoading}
loadingText="获取中"
{...inactiveButtonStyle}
>
</Button>
</>
)}
{/* 模式切换按钮组 */}
@@ -424,7 +473,7 @@ const KLineModule: React.FC<KLineModuleProps> = ({
<EmptyState title="暂无日K线数据" description="该股票暂无交易数据" />
)
) : (
// 分时走势图
// 分时走势图 + 五档盘口
minuteLoading ? (
<Center h="450px">
<VStack spacing={4}>
@@ -441,15 +490,115 @@ const KLineModule: React.FC<KLineModuleProps> = ({
</VStack>
</Center>
) : hasMinuteData ? (
<Box h="450px">
<ReactECharts
option={getMinuteKLineDarkGoldOption(minuteData)}
style={{ height: '100%', width: '100%' }}
theme="dark"
notMerge={true}
opts={{ renderer: 'canvas' }}
/>
</Box>
<Grid templateColumns={showOrderBook ? '1fr 220px' : '1fr'} gap={4} h="450px">
{/* 分时图表 */}
<GridItem>
<Box h="100%">
<ReactECharts
option={getMinuteKLineDarkGoldOption(minuteData)}
style={{ height: '100%', width: '100%' }}
theme="dark"
notMerge={true}
opts={{ renderer: 'canvas' }}
/>
</Box>
</GridItem>
{/* 五档盘口 */}
{showOrderBook && (
<GridItem>
<Box
h="100%"
bg="rgba(0, 0, 0, 0.3)"
borderRadius="lg"
border="1px solid"
borderColor={darkGoldTheme.border}
p={3}
overflowY="auto"
>
{/* 盘口标题 */}
<HStack justify="space-between" mb={3}>
<Text fontSize="sm" fontWeight="bold" color={darkGoldTheme.gold}>
</Text>
{/* 连接状态指示 */}
<HStack spacing={1}>
{isInTradingHours && (
<Badge
bg={connected.SSE || connected.SZSE ? 'green.500' : 'gray.500'}
color="white"
fontSize="2xs"
px={1}
>
{connected.SSE || connected.SZSE ? '实时' : '离线'}
</Badge>
)}
</HStack>
</HStack>
{/* 当前价格信息 */}
{currentQuote && (
<VStack spacing={1} mb={3} align="stretch">
<HStack justify="space-between">
<Text fontSize="xs" color={darkGoldTheme.textMuted}></Text>
<Text
fontSize="lg"
fontWeight="bold"
color={
currentQuote.changePct > 0 ? '#ff4d4d' :
currentQuote.changePct < 0 ? '#22c55e' :
darkGoldTheme.textPrimary
}
>
{currentQuote.price?.toFixed(2) || '-'}
</Text>
</HStack>
<HStack justify="space-between">
<Text fontSize="xs" color={darkGoldTheme.textMuted}></Text>
<Text
fontSize="sm"
color={
currentQuote.changePct > 0 ? '#ff4d4d' :
currentQuote.changePct < 0 ? '#22c55e' :
darkGoldTheme.textMuted
}
>
{currentQuote.changePct > 0 ? '+' : ''}{currentQuote.changePct?.toFixed(2) || '0.00'}%
</Text>
</HStack>
</VStack>
)}
{/* 五档盘口面板 */}
{currentQuote && (currentQuote.bidPrices?.length > 0 || currentQuote.askPrices?.length > 0) ? (
<OrderBookPanel
bidPrices={currentQuote.bidPrices || []}
bidVolumes={currentQuote.bidVolumes || []}
askPrices={currentQuote.askPrices || []}
askVolumes={currentQuote.askVolumes || []}
prevClose={currentQuote.prevClose}
upperLimit={'upperLimit' in currentQuote ? currentQuote.upperLimit : undefined}
lowerLimit={'lowerLimit' in currentQuote ? currentQuote.lowerLimit : undefined}
defaultLevels={5}
/>
) : (
<Center h="200px">
<VStack spacing={2}>
<Text fontSize="xs" color={darkGoldTheme.textMuted}>
{isInTradingHours ? '获取盘口数据中...' : '非交易时间'}
</Text>
{!isInTradingHours && (
<Text fontSize="2xs" color={darkGoldTheme.textMuted}>
交易时间: 9:30-11:30, 13:00-15:00
</Text>
)}
</VStack>
</Center>
)}
</Box>
</GridItem>
)}
</Grid>
) : (
<EmptyState title="暂无分时数据" description="点击刷新按钮获取当日分时数据" />
)

View File

@@ -85,7 +85,7 @@ const MetricOverlaySearch: React.FC<MetricOverlaySearchProps> = ({
setIsSearching(true);
try {
const response = await searchMetrics(query, undefined, undefined, 20);
const response = await searchMetrics(query, undefined, '日', 20);
setSearchResults(response.results || []);
} catch (error) {
console.error('Search metrics failed:', error);

View File

@@ -16,6 +16,7 @@ export interface TradeDataPanelProps {
onChartClick: (params: { seriesName?: string; data?: [number, number] }) => void;
selectedPeriod?: number;
onPeriodChange?: (period: number) => void;
stockCode?: string;
}
const TradeDataPanel: React.FC<TradeDataPanelProps> = ({
@@ -28,6 +29,7 @@ const TradeDataPanel: React.FC<TradeDataPanelProps> = ({
onChartClick,
selectedPeriod,
onPeriodChange,
stockCode,
}) => {
return (
<KLineModule
@@ -40,6 +42,7 @@ const TradeDataPanel: React.FC<TradeDataPanelProps> = ({
onChartClick={onChartClick}
selectedPeriod={selectedPeriod}
onPeriodChange={onPeriodChange}
stockCode={stockCode}
/>
);
};

View File

@@ -153,6 +153,7 @@ const MarketDataView: React.FC<MarketDataViewProps> = ({ stockCode: propStockCod
onChartClick={handleChartClick}
selectedPeriod={selectedPeriod}
onPeriodChange={setSelectedPeriod}
stockCode={stockCode}
/>
)}

View File

@@ -299,6 +299,7 @@ export interface KLineModuleProps {
onChartClick: (params: { seriesName?: string; data?: [number, number] }) => void;
selectedPeriod?: number;
onPeriodChange?: (period: number) => void;
stockCode?: string; // 股票代码,用于获取实时盘口数据
}
/**