update pay ui

This commit is contained in:
2025-12-11 10:07:17 +08:00
parent d0c9d9b1fb
commit 29cf0d7013
5 changed files with 110 additions and 84 deletions

View File

@@ -256,10 +256,14 @@ const MiniTimelineChart: React.FC<MiniTimelineChartProps> = ({
return () => window.removeEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize);
}, []); }, []);
// 深色主题颜色
const subTextColor = 'rgba(255, 255, 255, 0.5)';
const accentColor = '#8b5cf6';
if (loading) { if (loading) {
return ( return (
<Center h={height}> <Center h={height}>
<Spinner size="sm" color="gray.400" /> <Spinner size="sm" color={accentColor} />
</Center> </Center>
); );
} }
@@ -267,7 +271,7 @@ const MiniTimelineChart: React.FC<MiniTimelineChartProps> = ({
if (error || !chartData.length) { if (error || !chartData.length) {
return ( return (
<Center h={height}> <Center h={height}>
<Text fontSize="xs" color="gray.400"> <Text fontSize="xs" color={subTextColor}>
{error || '暂无数据'} {error || '暂无数据'}
</Text> </Text>
</Center> </Center>

View File

@@ -13,7 +13,6 @@ import {
Text, Text,
Button, Button,
ButtonGroup, ButtonGroup,
useColorModeValue,
Tooltip, Tooltip,
Badge, Badge,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
@@ -41,26 +40,26 @@ const formatVolume = (volume: number): string => {
}; };
/** /**
* 格式化价格 * 格式化价格 - 深色主题
*/ */
const formatPrice = (price: number, prevClose?: number): FormattedPrice => { const formatPrice = (price: number, prevClose?: number): FormattedPrice => {
if (!price || price === 0) { if (!price || price === 0) {
return { text: '-', color: 'gray.400' }; return { text: '-', color: 'rgba(255, 255, 255, 0.4)' };
} }
const text = price.toFixed(2); const text = price.toFixed(2);
if (!prevClose || prevClose === 0) { if (!prevClose || prevClose === 0) {
return { text, color: 'gray.600' }; return { text, color: 'rgba(255, 255, 255, 0.6)' };
} }
if (price > prevClose) { if (price > prevClose) {
return { text, color: 'red.500' }; return { text, color: '#ff4d4d' };
} }
if (price < prevClose) { if (price < prevClose) {
return { text, color: 'green.500' }; return { text, color: '#22c55e' };
} }
return { text, color: 'gray.600' }; return { text, color: 'rgba(255, 255, 255, 0.6)' };
}; };
/** OrderRow 组件 Props */ /** OrderRow 组件 Props */
@@ -75,7 +74,7 @@ interface OrderRowProps {
} }
/** /**
* 单行盘口 * 单行盘口 - 深色主题
*/ */
const OrderRow: React.FC<OrderRowProps> = ({ const OrderRow: React.FC<OrderRowProps> = ({
label, label,
@@ -86,15 +85,11 @@ const OrderRow: React.FC<OrderRowProps> = ({
maxVolume, maxVolume,
isLimitPrice, isLimitPrice,
}) => { }) => {
const bgColor = useColorModeValue( // 深色主题颜色
isBid ? 'red.50' : 'green.50', const barColor = isBid ? 'rgba(255, 77, 77, 0.2)' : 'rgba(34, 197, 94, 0.2)';
isBid ? 'rgba(239, 68, 68, 0.1)' : 'rgba(34, 197, 94, 0.1)' const limitColor = 'orange.300';
); const labelColor = 'rgba(255, 255, 255, 0.5)';
const barColor = useColorModeValue( const volumeColor = 'rgba(255, 255, 255, 0.6)';
isBid ? 'red.200' : 'green.200',
isBid ? 'rgba(239, 68, 68, 0.3)' : 'rgba(34, 197, 94, 0.3)'
);
const limitColor = useColorModeValue('orange.500', 'orange.300');
const priceInfo = formatPrice(price, prevClose); const priceInfo = formatPrice(price, prevClose);
const volumeText = formatVolume(volume); const volumeText = formatVolume(volume);
@@ -123,7 +118,7 @@ const OrderRow: React.FC<OrderRowProps> = ({
/> />
{/* 内容 */} {/* 内容 */}
<Text color="gray.500" w="24px" flexShrink={0} zIndex={1}> <Text color={labelColor} w="24px" flexShrink={0} zIndex={1}>
{label} {label}
</Text> </Text>
<HStack flex={1} justify="flex-end" zIndex={1}> <HStack flex={1} justify="flex-end" zIndex={1}>
@@ -142,7 +137,7 @@ const OrderRow: React.FC<OrderRowProps> = ({
</Tooltip> </Tooltip>
)} )}
</HStack> </HStack>
<Text color="gray.600" w="40px" textAlign="right" zIndex={1}> <Text color={volumeColor} w="40px" textAlign="right" zIndex={1}>
{volumeText} {volumeText}
</Text> </Text>
</HStack> </HStack>
@@ -150,7 +145,7 @@ const OrderRow: React.FC<OrderRowProps> = ({
}; };
/** /**
* OrderBookPanel 组件 * OrderBookPanel 组件 - 深色主题
*/ */
const OrderBookPanel: React.FC<OrderBookPanelProps> = ({ const OrderBookPanel: React.FC<OrderBookPanelProps> = ({
bidPrices = [], bidPrices = [],
@@ -162,9 +157,11 @@ const OrderBookPanel: React.FC<OrderBookPanelProps> = ({
lowerLimit, lowerLimit,
defaultLevels = 5, defaultLevels = 5,
}) => { }) => {
const borderColor = useColorModeValue('gray.200', 'gray.700'); // 深色主题颜色
const buttonBg = useColorModeValue('gray.100', 'gray.700'); const borderColor = 'rgba(255, 255, 255, 0.1)';
const bgColor = useColorModeValue('white', '#1a1a1a'); const buttonBg = 'rgba(255, 255, 255, 0.1)';
const bgColor = 'transparent';
const textColor = 'rgba(255, 255, 255, 0.6)';
// 可切换显示的档位数 // 可切换显示的档位数
const maxAvailableLevels = Math.max(bidPrices.length, askPrices.length, 1); const maxAvailableLevels = Math.max(bidPrices.length, askPrices.length, 1);
@@ -230,7 +227,7 @@ const OrderBookPanel: React.FC<OrderBookPanelProps> = ({
if (!hasData) { if (!hasData) {
return ( return (
<Box textAlign="center" py={2}> <Box textAlign="center" py={2}>
<Text fontSize="xs" color="gray.400"> <Text fontSize="xs" color={textColor}>
</Text> </Text>
</Box> </Box>
@@ -246,14 +243,20 @@ const OrderBookPanel: React.FC<OrderBookPanelProps> = ({
<Button <Button
onClick={() => setShowLevels(5)} onClick={() => setShowLevels(5)}
bg={showLevels === 5 ? buttonBg : 'transparent'} bg={showLevels === 5 ? buttonBg : 'transparent'}
color={textColor}
borderColor={borderColor}
fontWeight={showLevels === 5 ? 'bold' : 'normal'} fontWeight={showLevels === 5 ? 'bold' : 'normal'}
_hover={{ bg: 'rgba(255, 255, 255, 0.15)' }}
> >
5 5
</Button> </Button>
<Button <Button
onClick={() => setShowLevels(10)} onClick={() => setShowLevels(10)}
bg={showLevels === 10 ? buttonBg : 'transparent'} bg={showLevels === 10 ? buttonBg : 'transparent'}
color={textColor}
borderColor={borderColor}
fontWeight={showLevels === 10 ? 'bold' : 'normal'} fontWeight={showLevels === 10 ? 'bold' : 'normal'}
_hover={{ bg: 'rgba(255, 255, 255, 0.15)' }}
> >
10 10
</Button> </Button>
@@ -273,7 +276,7 @@ const OrderBookPanel: React.FC<OrderBookPanelProps> = ({
top="50%" top="50%"
transform="translateY(-50%)" transform="translateY(-50%)"
fontSize="2xs" fontSize="2xs"
color="gray.400" color={textColor}
bg={bgColor} bg={bgColor}
px={1} px={1}
> >
@@ -287,7 +290,7 @@ const OrderBookPanel: React.FC<OrderBookPanelProps> = ({
{/* 涨跌停价信息 */} {/* 涨跌停价信息 */}
{(upperLimit || lowerLimit) && ( {(upperLimit || lowerLimit) && (
<HStack justify="space-between" mt={1} fontSize="2xs" color="gray.400"> <HStack justify="space-between" mt={1} fontSize="2xs" color={textColor}>
{lowerLimit && <Text> {lowerLimit.toFixed(2)}</Text>} {lowerLimit && <Text> {lowerLimit.toFixed(2)}</Text>}
{upperLimit && <Text> {upperLimit.toFixed(2)}</Text>} {upperLimit && <Text> {upperLimit.toFixed(2)}</Text>}
</HStack> </HStack>

View File

@@ -10,7 +10,6 @@ import {
Text, Text,
IconButton, IconButton,
Tooltip, Tooltip,
useColorModeValue,
Collapse, Collapse,
Badge, Badge,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
@@ -77,41 +76,27 @@ const QuoteTile: React.FC<QuoteTileProps> = ({
// 类型断言,确保类型安全 // 类型断言,确保类型安全
const quoteData = quote as Partial<QuoteData>; const quoteData = quote as Partial<QuoteData>;
// 色主题 // 色主题颜色 - 与 StockOverview 保持一致
const cardBg = useColorModeValue('white', '#1a1a1a'); const cardBg = 'rgba(255, 255, 255, 0.03)';
const borderColor = useColorModeValue('gray.200', '#333'); const borderColor = 'rgba(255, 255, 255, 0.08)';
const hoverBorderColor = useColorModeValue('purple.300', '#666'); const hoverBorderColor = '#8b5cf6';
const textColor = useColorModeValue('gray.800', 'white'); const textColor = 'rgba(255, 255, 255, 0.95)';
const subTextColor = useColorModeValue('gray.500', 'gray.400'); const subTextColor = 'rgba(255, 255, 255, 0.6)';
// 涨跌色 // 涨跌色
const { price, prevClose, change, changePct, amount } = quoteData; const { price, prevClose, change, changePct, amount } = quoteData;
const priceColor = useColorModeValue( const priceColor = !prevClose || price === prevClose
!prevClose || price === prevClose ? 'rgba(255, 255, 255, 0.7)'
? 'gray.800' : price && price > prevClose
: price && price > prevClose ? '#ff4d4d'
? 'red.500' : '#22c55e';
: 'green.500',
!prevClose || price === prevClose
? 'gray.200'
: price && price > prevClose
? 'red.400'
: 'green.400'
);
// 涨跌幅背景色 // 涨跌幅背景色
const changeBgColor = useColorModeValue( const changeBgColor = !changePct || changePct === 0
!changePct || changePct === 0 ? 'rgba(255, 255, 255, 0.1)'
? 'gray.100' : changePct > 0
: changePct > 0 ? 'rgba(255, 77, 77, 0.2)'
? 'red.100' : 'rgba(34, 197, 94, 0.2)';
: 'green.100',
!changePct || changePct === 0
? 'gray.700'
: changePct > 0
? 'rgba(239, 68, 68, 0.2)'
: 'rgba(34, 197, 94, 0.2)'
);
// 跳转到详情页 // 跳转到详情页
const handleNavigate = (): void => { const handleNavigate = (): void => {
@@ -156,9 +141,10 @@ const QuoteTile: React.FC<QuoteTileProps> = ({
borderRadius="lg" borderRadius="lg"
overflow="hidden" overflow="hidden"
transition="all 0.2s" transition="all 0.2s"
backdropFilter="blur(10px)"
_hover={{ _hover={{
borderColor: hoverBorderColor, borderColor: hoverBorderColor,
boxShadow: 'md', boxShadow: '0 4px 20px rgba(139, 92, 246, 0.2)',
}} }}
> >
{/* 头部 */} {/* 头部 */}
@@ -227,6 +213,8 @@ const QuoteTile: React.FC<QuoteTileProps> = ({
icon={expanded ? <ChevronUpIcon /> : <ChevronDownIcon />} icon={expanded ? <ChevronUpIcon /> : <ChevronDownIcon />}
size="xs" size="xs"
variant="ghost" variant="ghost"
color={subTextColor}
_hover={{ bg: 'rgba(255, 255, 255, 0.1)', color: textColor }}
aria-label={expanded ? '收起' : '展开'} aria-label={expanded ? '收起' : '展开'}
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
@@ -238,7 +226,8 @@ const QuoteTile: React.FC<QuoteTileProps> = ({
icon={<CloseIcon />} icon={<CloseIcon />}
size="xs" size="xs"
variant="ghost" variant="ghost"
colorScheme="red" color="red.400"
_hover={{ bg: 'rgba(239, 68, 68, 0.1)' }}
aria-label="移除" aria-label="移除"
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();

View File

@@ -27,7 +27,6 @@ import {
Flex, Flex,
Spacer, Spacer,
Icon, Icon,
useColorModeValue,
useToast, useToast,
Badge, Badge,
Tooltip, Tooltip,
@@ -124,13 +123,14 @@ const FlexScreen: React.FC = () => {
// 面板状态 // 面板状态
const [isCollapsed, setIsCollapsed] = useState(false); const [isCollapsed, setIsCollapsed] = useState(false);
// 色主题 // 色主题颜色 - 与 StockOverview 保持一致
const cardBg = useColorModeValue('white', '#1a1a1a'); const cardBg = 'rgba(255, 255, 255, 0.03)';
const borderColor = useColorModeValue('gray.200', '#333'); const borderColor = 'rgba(255, 255, 255, 0.08)';
const textColor = useColorModeValue('gray.800', 'white'); const textColor = 'rgba(255, 255, 255, 0.95)';
const subTextColor = useColorModeValue('gray.600', 'gray.400'); const subTextColor = 'rgba(255, 255, 255, 0.6)';
const searchBg = useColorModeValue('gray.50', '#2a2a2a'); const searchBg = 'rgba(255, 255, 255, 0.05)';
const hoverBg = useColorModeValue('gray.100', '#333'); const hoverBg = 'rgba(255, 255, 255, 0.08)';
const accentColor = '#8b5cf6';
// 获取订阅的证券代码列表带后缀用于区分上证指数000001.SH和平安银行000001.SZ // 获取订阅的证券代码列表带后缀用于区分上证指数000001.SH和平安银行000001.SZ
const subscribedCodes = useMemo(() => { const subscribedCodes = useMemo(() => {
@@ -287,12 +287,18 @@ const FlexScreen: React.FC = () => {
}, [connected]); }, [connected]);
return ( return (
<Card bg={cardBg} borderWidth="1px" borderColor={borderColor}> <Card
bg={cardBg}
borderWidth="1px"
borderColor={borderColor}
backdropFilter="blur(20px)"
borderRadius="16px"
>
<CardBody> <CardBody>
{/* 头部 */} {/* 头部 */}
<Flex align="center" mb={4}> <Flex align="center" mb={4}>
<HStack spacing={3}> <HStack spacing={3}>
<Icon as={FaDesktop} boxSize={6} color="purple.500" /> <Icon as={FaDesktop} boxSize={6} color={accentColor} />
<Heading size="md" color={textColor}> <Heading size="md" color={textColor}>
</Heading> </Heading>
@@ -318,13 +324,27 @@ const FlexScreen: React.FC = () => {
icon={<SettingsIcon />} icon={<SettingsIcon />}
size="sm" size="sm"
variant="ghost" variant="ghost"
color={subTextColor}
_hover={{ bg: hoverBg, color: textColor }}
aria-label="设置" aria-label="设置"
/> />
<MenuList> <MenuList bg="#1a1a2e" borderColor={borderColor}>
<MenuItem icon={<FaSync />} onClick={resetWatchlist}> <MenuItem
icon={<FaSync />}
onClick={resetWatchlist}
bg="transparent"
color={textColor}
_hover={{ bg: hoverBg }}
>
</MenuItem> </MenuItem>
<MenuItem icon={<FaTrash />} onClick={clearWatchlist} color="red.500"> <MenuItem
icon={<FaTrash />}
onClick={clearWatchlist}
bg="transparent"
color="red.400"
_hover={{ bg: 'rgba(239, 68, 68, 0.1)' }}
>
</MenuItem> </MenuItem>
</MenuList> </MenuList>
@@ -334,6 +354,8 @@ const FlexScreen: React.FC = () => {
icon={isCollapsed ? <ChevronDownIcon /> : <ChevronUpIcon />} icon={isCollapsed ? <ChevronDownIcon /> : <ChevronUpIcon />}
size="sm" size="sm"
variant="ghost" variant="ghost"
color={subTextColor}
_hover={{ bg: hoverBg, color: textColor }}
onClick={() => setIsCollapsed(!isCollapsed)} onClick={() => setIsCollapsed(!isCollapsed)}
aria-label={isCollapsed ? '展开' : '收起'} aria-label={isCollapsed ? '展开' : '收起'}
/> />
@@ -346,7 +368,7 @@ const FlexScreen: React.FC = () => {
<Box position="relative" mb={4}> <Box position="relative" mb={4}>
<InputGroup size="md"> <InputGroup size="md">
<InputLeftElement pointerEvents="none"> <InputLeftElement pointerEvents="none">
<SearchIcon color="gray.400" /> <SearchIcon color={subTextColor} />
</InputLeftElement> </InputLeftElement>
<Input <Input
placeholder="搜索股票/指数代码或名称..." placeholder="搜索股票/指数代码或名称..."
@@ -354,9 +376,13 @@ const FlexScreen: React.FC = () => {
onChange={e => setSearchQuery(e.target.value)} onChange={e => setSearchQuery(e.target.value)}
bg={searchBg} bg={searchBg}
borderRadius="lg" borderRadius="lg"
borderColor={borderColor}
color={textColor}
_placeholder={{ color: subTextColor }}
_hover={{ borderColor: accentColor }}
_focus={{ _focus={{
borderColor: 'purple.400', borderColor: accentColor,
boxShadow: '0 0 0 1px var(--chakra-colors-purple-400)', boxShadow: `0 0 0 1px ${accentColor}`,
}} }}
/> />
{searchQuery && ( {searchQuery && (
@@ -365,6 +391,8 @@ const FlexScreen: React.FC = () => {
size="sm" size="sm"
icon={<CloseIcon />} icon={<CloseIcon />}
variant="ghost" variant="ghost"
color={subTextColor}
_hover={{ bg: hoverBg, color: textColor }}
onClick={() => { onClick={() => {
setSearchQuery(''); setSearchQuery('');
setShowResults(false); setShowResults(false);
@@ -383,18 +411,18 @@ const FlexScreen: React.FC = () => {
left={0} left={0}
right={0} right={0}
mt={1} mt={1}
bg={cardBg} bg="#1a1a2e"
borderWidth="1px" borderWidth="1px"
borderColor={borderColor} borderColor={borderColor}
borderRadius="lg" borderRadius="lg"
boxShadow="lg" boxShadow="0 8px 32px rgba(0, 0, 0, 0.5)"
maxH="300px" maxH="300px"
overflowY="auto" overflowY="auto"
zIndex={10} zIndex={10}
> >
{isSearching ? ( {isSearching ? (
<Center p={4}> <Center p={4}>
<Spinner size="sm" color="purple.500" /> <Spinner size="sm" color={accentColor} />
</Center> </Center>
) : searchResults.length > 0 ? ( ) : searchResults.length > 0 ? (
<List spacing={0}> <List spacing={0}>
@@ -460,10 +488,11 @@ const FlexScreen: React.FC = () => {
<Tag <Tag
key={item.code} key={item.code}
size="md" size="md"
variant="subtle" variant="outline"
colorScheme="purple" borderColor={accentColor}
color={textColor}
cursor="pointer" cursor="pointer"
_hover={{ bg: 'purple.100' }} _hover={{ bg: 'rgba(139, 92, 246, 0.2)' }}
onClick={() => addSecurity(item)} onClick={() => addSecurity(item)}
> >
<TagLabel>{item.name}</TagLabel> <TagLabel>{item.name}</TagLabel>
@@ -493,7 +522,7 @@ const FlexScreen: React.FC = () => {
) : ( ) : (
<Center py={8}> <Center py={8}>
<VStack spacing={3}> <VStack spacing={3}>
<Icon as={FaExclamationCircle} boxSize={10} color="gray.300" /> <Icon as={FaExclamationCircle} boxSize={10} color={subTextColor} />
<Text color={subTextColor}></Text> <Text color={subTextColor}></Text>
</VStack> </VStack>
</Center> </Center>

View File

@@ -852,6 +852,7 @@ const StockOverview = () => {
minDate={tradingDays.length > 0 ? new Date(tradingDays[0]) : undefined} minDate={tradingDays.length > 0 ? new Date(tradingDays[0]) : undefined}
maxDate={tradingDays.length > 0 ? new Date(tradingDays[tradingDays.length - 1]) : undefined} maxDate={tradingDays.length > 0 ? new Date(tradingDays[tradingDays.length - 1]) : undefined}
label="交易日期" label="交易日期"
isDarkMode={true}
/> />
</Flex> </Flex>
{selectedDate && ( {selectedDate && (