update pay promo
This commit is contained in:
@@ -1,7 +1,8 @@
|
|||||||
// src/components/Navbars/SearchBar/SearchBar.js
|
// src/components/Navbars/SearchBar/SearchBar.js
|
||||||
// 全局股票搜索栏 - 模糊搜索 + 下拉选择
|
// 全局股票搜索栏 - 模糊搜索 + 下拉选择
|
||||||
|
// 设计风格:玻璃态融合 + 微妙金色点缀
|
||||||
|
|
||||||
import React, { useRef, useEffect, useCallback } from "react";
|
import React, { useRef, useEffect, useCallback, useState } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
@@ -27,16 +28,14 @@ export function SearchBar(props) {
|
|||||||
const { variant, children, ...rest } = props;
|
const { variant, children, ...rest } = props;
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const containerRef = useRef(null);
|
const containerRef = useRef(null);
|
||||||
|
const [isFocused, setIsFocused] = useState(false);
|
||||||
|
|
||||||
// 颜色配置 - 固定使用深色主题(增强可见性)
|
// 颜色配置 - 玻璃态风格,低调但醒目
|
||||||
const searchIconColor = "#D4AF37";
|
const accentColor = "#D4AF37"; // 金色强调
|
||||||
const inputBg = "rgba(26, 32, 44, 0.9)";
|
const dropdownBg = "rgba(20, 25, 35, 0.98)";
|
||||||
const dropdownBg = "#1a1a2e";
|
|
||||||
const borderColor = "rgba(212, 175, 55, 0.5)";
|
|
||||||
const hoverBg = "whiteAlpha.100";
|
const hoverBg = "whiteAlpha.100";
|
||||||
const textColor = "white";
|
const textColor = "white";
|
||||||
const subTextColor = "whiteAlpha.600";
|
const subTextColor = "whiteAlpha.600";
|
||||||
const accentColor = "#D4AF37";
|
|
||||||
|
|
||||||
// 使用搜索 Hook
|
// 使用搜索 Hook
|
||||||
const {
|
const {
|
||||||
@@ -54,6 +53,7 @@ export function SearchBar(props) {
|
|||||||
const handleClickOutside = (event) => {
|
const handleClickOutside = (event) => {
|
||||||
if (containerRef.current && !containerRef.current.contains(event.target)) {
|
if (containerRef.current && !containerRef.current.contains(event.target)) {
|
||||||
setShowResults(false);
|
setShowResults(false);
|
||||||
|
setIsFocused(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
document.addEventListener("mousedown", handleClickOutside);
|
document.addEventListener("mousedown", handleClickOutside);
|
||||||
@@ -63,6 +63,7 @@ export function SearchBar(props) {
|
|||||||
// 选择股票 - 跳转到详情页
|
// 选择股票 - 跳转到详情页
|
||||||
const handleSelectStock = useCallback((stock) => {
|
const handleSelectStock = useCallback((stock) => {
|
||||||
clearSearch();
|
clearSearch();
|
||||||
|
setIsFocused(false);
|
||||||
// 跳转到股票详情页
|
// 跳转到股票详情页
|
||||||
navigate(`/company/${stock.stock_code}`);
|
navigate(`/company/${stock.stock_code}`);
|
||||||
}, [navigate, clearSearch]);
|
}, [navigate, clearSearch]);
|
||||||
@@ -73,95 +74,148 @@ export function SearchBar(props) {
|
|||||||
handleSelectStock(searchResults[0]);
|
handleSelectStock(searchResults[0]);
|
||||||
} else if (e.key === "Escape") {
|
} else if (e.key === "Escape") {
|
||||||
setShowResults(false);
|
setShowResults(false);
|
||||||
|
setIsFocused(false);
|
||||||
}
|
}
|
||||||
}, [searchResults, handleSelectStock, setShowResults]);
|
}, [searchResults, handleSelectStock, setShowResults]);
|
||||||
|
|
||||||
|
// 处理焦点
|
||||||
|
const handleFocus = () => {
|
||||||
|
setIsFocused(true);
|
||||||
|
if (searchQuery && searchResults.length > 0) {
|
||||||
|
setShowResults(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBlur = () => {
|
||||||
|
// 延迟关闭,让点击事件先执行
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!containerRef.current?.contains(document.activeElement)) {
|
||||||
|
setIsFocused(false);
|
||||||
|
}
|
||||||
|
}, 150);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box ref={containerRef} position="relative" {...rest}>
|
<Box ref={containerRef} position="relative" {...rest}>
|
||||||
<InputGroup borderRadius="8px" w={{ base: "180px", md: "220px", lg: "280px" }}>
|
{/* 搜索框容器 - 带微妙动画 */}
|
||||||
<InputLeftElement pointerEvents="none">
|
<Box
|
||||||
<Search color={searchIconColor} size={16} />
|
position="relative"
|
||||||
</InputLeftElement>
|
borderRadius="full"
|
||||||
<Input
|
bg={isFocused ? "rgba(255, 255, 255, 0.12)" : "rgba(255, 255, 255, 0.06)"}
|
||||||
variant="search"
|
backdropFilter="blur(10px)"
|
||||||
fontSize="sm"
|
border="1px solid"
|
||||||
bg={inputBg}
|
borderColor={isFocused ? "rgba(212, 175, 55, 0.4)" : "rgba(255, 255, 255, 0.1)"}
|
||||||
color="white"
|
boxShadow={isFocused ? "0 0 20px rgba(212, 175, 55, 0.15)" : "none"}
|
||||||
placeholder="搜索股票代码/名称..."
|
transition="all 0.3s cubic-bezier(0.4, 0, 0.2, 1)"
|
||||||
value={searchQuery}
|
_hover={{
|
||||||
onChange={(e) => handleSearch(e.target.value)}
|
bg: "rgba(255, 255, 255, 0.1)",
|
||||||
onKeyDown={handleKeyDown}
|
borderColor: "rgba(212, 175, 55, 0.3)",
|
||||||
onFocus={() => searchQuery && searchResults.length > 0 && setShowResults(true)}
|
}}
|
||||||
border="1px solid"
|
>
|
||||||
borderColor={borderColor}
|
<InputGroup w={{ base: "160px", md: "200px", lg: "240px" }}>
|
||||||
_placeholder={{ color: "gray.400" }}
|
<InputLeftElement pointerEvents="none" h="full">
|
||||||
_hover={{ borderColor: accentColor, bg: "rgba(26, 32, 44, 1)" }}
|
<Search
|
||||||
_focus={{
|
size={15}
|
||||||
borderColor: accentColor,
|
color={isFocused ? accentColor : "rgba(255, 255, 255, 0.5)"}
|
||||||
boxShadow: `0 0 0 1px ${accentColor}`,
|
style={{ transition: "color 0.3s" }}
|
||||||
bg: "rgba(26, 32, 44, 1)"
|
/>
|
||||||
}}
|
</InputLeftElement>
|
||||||
/>
|
<Input
|
||||||
{(searchQuery || isSearching) && (
|
variant="unstyled"
|
||||||
<InputRightElement>
|
fontSize="sm"
|
||||||
{isSearching ? (
|
color="white"
|
||||||
<Spinner size="sm" color={accentColor} />
|
placeholder="搜索股票..."
|
||||||
) : (
|
value={searchQuery}
|
||||||
<IconButton
|
onChange={(e) => handleSearch(e.target.value)}
|
||||||
size="xs"
|
onKeyDown={handleKeyDown}
|
||||||
variant="ghost"
|
onFocus={handleFocus}
|
||||||
icon={<X size={10} />}
|
onBlur={handleBlur}
|
||||||
onClick={clearSearch}
|
pl={10}
|
||||||
aria-label="清除搜索"
|
pr={searchQuery ? 10 : 4}
|
||||||
_hover={{ bg: "transparent" }}
|
py={2}
|
||||||
/>
|
h="36px"
|
||||||
)}
|
_placeholder={{
|
||||||
</InputRightElement>
|
color: "rgba(255, 255, 255, 0.4)",
|
||||||
)}
|
fontSize: "sm",
|
||||||
</InputGroup>
|
}}
|
||||||
|
/>
|
||||||
|
{(searchQuery || isSearching) && (
|
||||||
|
<InputRightElement h="full">
|
||||||
|
{isSearching ? (
|
||||||
|
<Spinner size="xs" color={accentColor} />
|
||||||
|
) : (
|
||||||
|
<IconButton
|
||||||
|
size="xs"
|
||||||
|
variant="ghost"
|
||||||
|
icon={<X size={12} color="rgba(255, 255, 255, 0.5)" />}
|
||||||
|
onClick={clearSearch}
|
||||||
|
aria-label="清除搜索"
|
||||||
|
borderRadius="full"
|
||||||
|
minW="auto"
|
||||||
|
h="20px"
|
||||||
|
w="20px"
|
||||||
|
_hover={{ bg: "whiteAlpha.200" }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</InputRightElement>
|
||||||
|
)}
|
||||||
|
</InputGroup>
|
||||||
|
</Box>
|
||||||
|
|
||||||
{/* 搜索结果下拉 */}
|
{/* 搜索结果下拉 - 玻璃态设计 */}
|
||||||
{showResults && (
|
{showResults && (
|
||||||
<Box
|
<Box
|
||||||
position="absolute"
|
position="absolute"
|
||||||
top="100%"
|
top="calc(100% + 8px)"
|
||||||
left={0}
|
left="50%"
|
||||||
mt={2}
|
transform="translateX(-50%)"
|
||||||
w="320px"
|
w="320px"
|
||||||
bg={dropdownBg}
|
bg={dropdownBg}
|
||||||
|
backdropFilter="blur(20px)"
|
||||||
border="1px solid"
|
border="1px solid"
|
||||||
borderColor={borderColor}
|
borderColor="rgba(212, 175, 55, 0.2)"
|
||||||
borderRadius="md"
|
borderRadius="xl"
|
||||||
boxShadow="lg"
|
boxShadow="0 10px 40px rgba(0, 0, 0, 0.4), 0 0 20px rgba(212, 175, 55, 0.1)"
|
||||||
maxH="400px"
|
maxH="400px"
|
||||||
overflowY="auto"
|
overflowY="auto"
|
||||||
zIndex={9999}
|
zIndex={9999}
|
||||||
|
css={{
|
||||||
|
'&::-webkit-scrollbar': { width: '4px' },
|
||||||
|
'&::-webkit-scrollbar-track': { background: 'transparent' },
|
||||||
|
'&::-webkit-scrollbar-thumb': {
|
||||||
|
background: 'rgba(212, 175, 55, 0.3)',
|
||||||
|
borderRadius: '2px',
|
||||||
|
},
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{searchResults.length > 0 ? (
|
{searchResults.length > 0 ? (
|
||||||
<List spacing={0}>
|
<List spacing={0} py={2}>
|
||||||
{searchResults.map((stock, index) => (
|
{searchResults.map((stock, index) => (
|
||||||
<ListItem
|
<ListItem
|
||||||
key={stock.stock_code}
|
key={stock.stock_code}
|
||||||
px={4}
|
px={4}
|
||||||
py={3}
|
py={2.5}
|
||||||
|
mx={2}
|
||||||
cursor="pointer"
|
cursor="pointer"
|
||||||
_hover={{ bg: hoverBg }}
|
borderRadius="lg"
|
||||||
|
transition="all 0.2s"
|
||||||
|
_hover={{
|
||||||
|
bg: "rgba(212, 175, 55, 0.1)",
|
||||||
|
}}
|
||||||
onClick={() => handleSelectStock(stock)}
|
onClick={() => handleSelectStock(stock)}
|
||||||
borderBottomWidth={index < searchResults.length - 1 ? "1px" : "0"}
|
|
||||||
borderColor={borderColor}
|
|
||||||
>
|
>
|
||||||
<Flex align="center" justify="space-between">
|
<Flex align="center" justify="space-between">
|
||||||
<VStack align="start" spacing={0} flex={1}>
|
<VStack align="start" spacing={0} flex={1}>
|
||||||
<Text fontWeight="bold" color={textColor} fontSize="sm">
|
<Text fontWeight="semibold" color={textColor} fontSize="sm">
|
||||||
{stock.stock_name}
|
{stock.stock_name}
|
||||||
</Text>
|
</Text>
|
||||||
<HStack spacing={2}>
|
<HStack spacing={2}>
|
||||||
<Text fontSize="xs" color={subTextColor}>
|
<Text fontSize="xs" color={accentColor} fontFamily="mono">
|
||||||
{stock.stock_code}
|
{stock.stock_code}
|
||||||
</Text>
|
</Text>
|
||||||
{stock.pinyin_abbr && (
|
{stock.pinyin_abbr && (
|
||||||
<Text fontSize="xs" color={subTextColor}>
|
<Text fontSize="xs" color={subTextColor}>
|
||||||
({stock.pinyin_abbr.toUpperCase()})
|
{stock.pinyin_abbr.toUpperCase()}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
</HStack>
|
</HStack>
|
||||||
@@ -169,9 +223,11 @@ export function SearchBar(props) {
|
|||||||
{stock.exchange && (
|
{stock.exchange && (
|
||||||
<Tag
|
<Tag
|
||||||
size="sm"
|
size="sm"
|
||||||
colorScheme="blue"
|
bg="rgba(212, 175, 55, 0.15)"
|
||||||
variant="subtle"
|
color={accentColor}
|
||||||
fontSize="xs"
|
fontSize="xs"
|
||||||
|
fontWeight="medium"
|
||||||
|
borderRadius="md"
|
||||||
>
|
>
|
||||||
{stock.exchange}
|
{stock.exchange}
|
||||||
</Tag>
|
</Tag>
|
||||||
@@ -181,10 +237,13 @@ export function SearchBar(props) {
|
|||||||
))}
|
))}
|
||||||
</List>
|
</List>
|
||||||
) : (
|
) : (
|
||||||
<Center p={4}>
|
<Center p={6}>
|
||||||
<Text color={subTextColor} fontSize="sm">
|
<VStack spacing={2}>
|
||||||
{searchQuery ? "未找到相关股票" : "输入股票代码或名称搜索"}
|
<Search size={24} color="rgba(255, 255, 255, 0.2)" />
|
||||||
</Text>
|
<Text color={subTextColor} fontSize="sm">
|
||||||
|
{searchQuery ? "未找到相关股票" : "输入代码或名称搜索"}
|
||||||
|
</Text>
|
||||||
|
</VStack>
|
||||||
</Center>
|
</Center>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -223,53 +223,12 @@ const [currentMode, setCurrentMode] = useState('vertical');
|
|||||||
setCurrentMode(mode);
|
setCurrentMode(mode);
|
||||||
}, [mode]);
|
}, [mode]);
|
||||||
|
|
||||||
// 看涨看跌投票处理
|
// 看涨看跌投票回调(API 调用已在卡片组件内部处理,这里仅用于可选的列表刷新)
|
||||||
const handleVoteChange = useCallback(async ({ eventId, voteType }) => {
|
const handleVoteChange = useCallback(({ eventId, voteType }) => {
|
||||||
if (!isLoggedIn) {
|
console.log('[DynamicNewsCard] 投票完成回调', { eventId, voteType });
|
||||||
toast({
|
// 投票已在 HorizontalDynamicNewsEventCard 组件内部处理
|
||||||
title: '请先登录',
|
// 这里可以选择是否刷新整个列表(暂不刷新,避免干扰用户体验)
|
||||||
description: '登录后才能参与投票',
|
}, []);
|
||||||
status: 'warning',
|
|
||||||
duration: 2000,
|
|
||||||
isClosable: true,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch(`${getApiBase()}/api/events/${eventId}/sentiment-vote`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
credentials: 'include',
|
|
||||||
body: JSON.stringify({ vote_type: voteType }),
|
|
||||||
});
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
|
|
||||||
if (!response.ok || !data.success) {
|
|
||||||
throw new Error(data.error || '投票失败');
|
|
||||||
}
|
|
||||||
|
|
||||||
toast({
|
|
||||||
title: voteType === 'bullish' ? '已看涨' : '已看跌',
|
|
||||||
status: 'success',
|
|
||||||
duration: 1500,
|
|
||||||
isClosable: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
// 刷新当前页数据以更新投票计数
|
|
||||||
handlePageChange(currentPage, true);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('投票失败:', error);
|
|
||||||
toast({
|
|
||||||
title: '投票失败',
|
|
||||||
description: error.message,
|
|
||||||
status: 'error',
|
|
||||||
duration: 2000,
|
|
||||||
isClosable: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [isLoggedIn, toast, handlePageChange, currentPage]);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ⚡【核心逻辑】执行刷新的回调函数(包含原有的智能刷新逻辑)
|
* ⚡【核心逻辑】执行刷新的回调函数(包含原有的智能刷新逻辑)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// src/views/Community/components/EventCard/HorizontalDynamicNewsEventCard.js
|
// src/views/Community/components/EventCard/HorizontalDynamicNewsEventCard.js
|
||||||
// 横向布局的动态新闻事件卡片组件(时间在左,卡片在右)
|
// 横向布局的动态新闻事件卡片组件(时间在左,卡片在右)
|
||||||
|
|
||||||
import React from 'react';
|
import React, { useState, useEffect, useCallback } from 'react';
|
||||||
import {
|
import {
|
||||||
HStack,
|
HStack,
|
||||||
Card,
|
Card,
|
||||||
@@ -12,10 +12,13 @@ import {
|
|||||||
Tooltip,
|
Tooltip,
|
||||||
Badge,
|
Badge,
|
||||||
useBreakpointValue,
|
useBreakpointValue,
|
||||||
|
useToast,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { getImportanceConfig } from '@constants/importanceLevels';
|
import { getImportanceConfig } from '@constants/importanceLevels';
|
||||||
import { PROFESSIONAL_COLORS } from '@constants/professionalTheme';
|
import { PROFESSIONAL_COLORS } from '@constants/professionalTheme';
|
||||||
import { useDevice } from '@hooks/useDevice';
|
import { useDevice } from '@hooks/useDevice';
|
||||||
|
import { useAuth } from '@contexts/AuthContext';
|
||||||
|
import { getApiBase } from '@utils/apiConfig';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
// 导入子组件
|
// 导入子组件
|
||||||
@@ -71,6 +74,93 @@ const HorizontalDynamicNewsEventCard = React.memo(({
|
|||||||
}) => {
|
}) => {
|
||||||
const importance = getImportanceConfig(event.importance);
|
const importance = getImportanceConfig(event.importance);
|
||||||
const { isMobile } = useDevice();
|
const { isMobile } = useDevice();
|
||||||
|
const { isAuthenticated: isLoggedIn } = useAuth();
|
||||||
|
const toast = useToast();
|
||||||
|
|
||||||
|
// 本地状态管理投票数据(实时更新 UI)
|
||||||
|
const [localBullish, setLocalBullish] = useState(event.bullish_count || 0);
|
||||||
|
const [localBearish, setLocalBearish] = useState(event.bearish_count || 0);
|
||||||
|
const [isVoting, setIsVoting] = useState(false);
|
||||||
|
|
||||||
|
// 同步 props 变化到本地状态
|
||||||
|
useEffect(() => {
|
||||||
|
setLocalBullish(event.bullish_count || 0);
|
||||||
|
}, [event.bullish_count]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setLocalBearish(event.bearish_count || 0);
|
||||||
|
}, [event.bearish_count]);
|
||||||
|
|
||||||
|
// 投票处理函数(直接调用 API 并更新本地状态)
|
||||||
|
const handleVote = useCallback(async (voteType) => {
|
||||||
|
if (!isLoggedIn) {
|
||||||
|
toast({
|
||||||
|
title: '请先登录',
|
||||||
|
description: '登录后才能参与投票',
|
||||||
|
status: 'warning',
|
||||||
|
duration: 2000,
|
||||||
|
isClosable: true,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isVoting) return;
|
||||||
|
setIsVoting(true);
|
||||||
|
|
||||||
|
// 乐观更新
|
||||||
|
const oldBullish = localBullish;
|
||||||
|
const oldBearish = localBearish;
|
||||||
|
if (voteType === 'bullish') {
|
||||||
|
setLocalBullish(prev => prev + 1);
|
||||||
|
} else {
|
||||||
|
setLocalBearish(prev => prev + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${getApiBase()}/api/events/${event.id}/sentiment-vote`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
credentials: 'include',
|
||||||
|
body: JSON.stringify({ vote_type: voteType }),
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (!response.ok || !data.success) {
|
||||||
|
throw new Error(data.error || '投票失败');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用服务端返回的真实数据更新
|
||||||
|
if (data.data) {
|
||||||
|
setLocalBullish(data.data.bullish_count ?? localBullish);
|
||||||
|
setLocalBearish(data.data.bearish_count ?? localBearish);
|
||||||
|
}
|
||||||
|
|
||||||
|
toast({
|
||||||
|
title: voteType === 'bullish' ? '已看涨' : '已看跌',
|
||||||
|
status: 'success',
|
||||||
|
duration: 1500,
|
||||||
|
isClosable: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 通知父组件(可选,用于刷新列表)
|
||||||
|
onVoteChange?.({ eventId: event.id, voteType });
|
||||||
|
} catch (error) {
|
||||||
|
// 回滚
|
||||||
|
setLocalBullish(oldBullish);
|
||||||
|
setLocalBearish(oldBearish);
|
||||||
|
|
||||||
|
toast({
|
||||||
|
title: '投票失败',
|
||||||
|
description: error.message,
|
||||||
|
status: 'error',
|
||||||
|
duration: 2000,
|
||||||
|
isClosable: true,
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setIsVoting(false);
|
||||||
|
}
|
||||||
|
}, [event.id, isLoggedIn, isVoting, localBullish, localBearish, toast, onVoteChange]);
|
||||||
|
|
||||||
// 专业配色 - 黑色、灰色、金色主题
|
// 专业配色 - 黑色、灰色、金色主题
|
||||||
const cardBg = PROFESSIONAL_COLORS.background.card;
|
const cardBg = PROFESSIONAL_COLORS.background.card;
|
||||||
@@ -275,11 +365,13 @@ const HorizontalDynamicNewsEventCard = React.memo(({
|
|||||||
spacing={0}
|
spacing={0}
|
||||||
_hover={{ bg: '#9B2C2C' }}
|
_hover={{ bg: '#9B2C2C' }}
|
||||||
transition="all 0.2s"
|
transition="all 0.2s"
|
||||||
onClick={() => onVoteChange?.({ eventId: event.id, voteType: 'bullish' })}
|
onClick={() => handleVote('bullish')}
|
||||||
|
disabled={isVoting}
|
||||||
|
opacity={isVoting ? 0.7 : 1}
|
||||||
>
|
>
|
||||||
<HStack spacing={1}>
|
<HStack spacing={1}>
|
||||||
<TrendingUp size={16} />
|
<TrendingUp size={16} />
|
||||||
<Text fontSize="sm" fontWeight="semibold">{formatCompactNumber(event.bullish_count)}</Text>
|
<Text fontSize="sm" fontWeight="semibold">{formatCompactNumber(localBullish)}</Text>
|
||||||
</HStack>
|
</HStack>
|
||||||
<Text fontSize="xs">看涨</Text>
|
<Text fontSize="xs">看涨</Text>
|
||||||
</VStack>
|
</VStack>
|
||||||
@@ -296,11 +388,13 @@ const HorizontalDynamicNewsEventCard = React.memo(({
|
|||||||
spacing={0}
|
spacing={0}
|
||||||
_hover={{ bg: '#2F855A' }}
|
_hover={{ bg: '#2F855A' }}
|
||||||
transition="all 0.2s"
|
transition="all 0.2s"
|
||||||
onClick={() => onVoteChange?.({ eventId: event.id, voteType: 'bearish' })}
|
onClick={() => handleVote('bearish')}
|
||||||
|
disabled={isVoting}
|
||||||
|
opacity={isVoting ? 0.7 : 1}
|
||||||
>
|
>
|
||||||
<HStack spacing={1}>
|
<HStack spacing={1}>
|
||||||
<TrendingDown size={16} />
|
<TrendingDown size={16} />
|
||||||
<Text fontSize="sm" fontWeight="semibold">{formatCompactNumber(event.bearish_count)}</Text>
|
<Text fontSize="sm" fontWeight="semibold">{formatCompactNumber(localBearish)}</Text>
|
||||||
</HStack>
|
</HStack>
|
||||||
<Text fontSize="xs">看跌</Text>
|
<Text fontSize="xs">看跌</Text>
|
||||||
</VStack>
|
</VStack>
|
||||||
|
|||||||
Reference in New Issue
Block a user