更新Company页面的UI为FUI风格
This commit is contained in:
@@ -24,7 +24,9 @@ import {
|
||||
Button,
|
||||
} from "@chakra-ui/react";
|
||||
import { ChevronDownIcon, ChevronUpIcon, RepeatIcon } from "@chakra-ui/icons";
|
||||
import { FiTrendingUp, FiZap } from "react-icons/fi";
|
||||
import { FiTrendingUp, FiZap, FiClock } from "react-icons/fi";
|
||||
import { FireOutlined } from "@ant-design/icons";
|
||||
import dayjs from "dayjs";
|
||||
import { Select } from "antd";
|
||||
import MiniEventCard from "../../EventCard/MiniEventCard";
|
||||
import { getApiBase } from "@utils/apiConfig";
|
||||
@@ -47,6 +49,122 @@ const COLORS = {
|
||||
// 每次加载的事件数量
|
||||
const EVENTS_PER_LOAD = 12;
|
||||
|
||||
/**
|
||||
* 格式化时间显示
|
||||
*/
|
||||
const formatEventTime = (dateStr) => {
|
||||
if (!dateStr) return "";
|
||||
const date = dayjs(dateStr);
|
||||
const now = dayjs();
|
||||
const isToday = date.isSame(now, "day");
|
||||
const isYesterday = date.isSame(now.subtract(1, "day"), "day");
|
||||
|
||||
if (isToday) {
|
||||
return date.format("HH:mm");
|
||||
} else if (isYesterday) {
|
||||
return `昨天 ${date.format("HH:mm")}`;
|
||||
} else {
|
||||
return date.format("MM-DD HH:mm");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 单个事件项组件 - 带时间轴
|
||||
*/
|
||||
const TimelineEventItem = React.memo(({ event, isSelected, onEventClick, isHot }) => {
|
||||
const changePct = event.max_change_pct ?? event.change_pct;
|
||||
const hasChange = changePct != null;
|
||||
const isPositive = hasChange && changePct >= 0;
|
||||
|
||||
return (
|
||||
<HStack
|
||||
spacing={0}
|
||||
align="stretch"
|
||||
w="100%"
|
||||
cursor="pointer"
|
||||
onClick={() => onEventClick?.(event)}
|
||||
_hover={{ bg: "rgba(255, 255, 255, 0.05)" }}
|
||||
borderRadius="md"
|
||||
transition="all 0.15s"
|
||||
bg={isSelected ? "rgba(66, 153, 225, 0.15)" : "transparent"}
|
||||
>
|
||||
{/* 左侧时间轴 */}
|
||||
<VStack spacing={0} align="center" w="50px" flexShrink={0} position="relative">
|
||||
{/* 时间显示 */}
|
||||
<Text fontSize="xs" color={COLORS.secondaryTextColor} fontWeight="500">
|
||||
{formatEventTime(event.created_at || event.event_time)}
|
||||
</Text>
|
||||
{/* 时间轴线 */}
|
||||
<Box
|
||||
w="2px"
|
||||
flex={1}
|
||||
bg="rgba(255, 255, 255, 0.1)"
|
||||
mt={1}
|
||||
minH="20px"
|
||||
/>
|
||||
{/* 时间轴圆点 */}
|
||||
<Box
|
||||
position="absolute"
|
||||
top="18px"
|
||||
w="6px"
|
||||
h="6px"
|
||||
borderRadius="full"
|
||||
bg={isHot ? "#f56565" : `${COLORS.secondaryTextColor}`}
|
||||
border="2px solid"
|
||||
borderColor={COLORS.cardBg}
|
||||
/>
|
||||
</VStack>
|
||||
|
||||
{/* 右侧内容 */}
|
||||
<Box flex={1} py={1} pr={2} minW={0}>
|
||||
<HStack spacing={1} align="start">
|
||||
{/* HOT 标签 */}
|
||||
{isHot && (
|
||||
<Badge
|
||||
bg="linear-gradient(135deg, #f56565 0%, #ed8936 100%)"
|
||||
color="white"
|
||||
fontSize="10px"
|
||||
px={1}
|
||||
py={0}
|
||||
borderRadius="sm"
|
||||
flexShrink={0}
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
gap="2px"
|
||||
>
|
||||
<FireOutlined style={{ fontSize: 10 }} />
|
||||
HOT
|
||||
</Badge>
|
||||
)}
|
||||
<Text
|
||||
fontSize="xs"
|
||||
color={COLORS.textColor}
|
||||
noOfLines={2}
|
||||
flex={1}
|
||||
lineHeight="1.4"
|
||||
>
|
||||
{event.title}
|
||||
</Text>
|
||||
{/* 涨跌幅 */}
|
||||
{hasChange && (
|
||||
<Text
|
||||
fontSize="xs"
|
||||
fontWeight="bold"
|
||||
color={isPositive ? "#fc8181" : "#68d391"}
|
||||
flexShrink={0}
|
||||
>
|
||||
{isPositive ? "+" : ""}
|
||||
{changePct.toFixed(1)}%
|
||||
</Text>
|
||||
)}
|
||||
</HStack>
|
||||
</Box>
|
||||
</HStack>
|
||||
);
|
||||
});
|
||||
|
||||
TimelineEventItem.displayName = "TimelineEventItem";
|
||||
|
||||
/**
|
||||
* 单个主线卡片组件 - 支持懒加载
|
||||
*/
|
||||
@@ -70,6 +188,23 @@ const MainlineCard = React.memo(
|
||||
}
|
||||
}, [isExpanded]);
|
||||
|
||||
// 找出涨幅最大的事件(HOT 事件)
|
||||
const hotEvent = useMemo(() => {
|
||||
if (!mainline.events || mainline.events.length === 0) return null;
|
||||
let maxChange = -Infinity;
|
||||
let hot = null;
|
||||
mainline.events.forEach((event) => {
|
||||
const change = event.max_change_pct ?? event.change_pct ?? -Infinity;
|
||||
if (change > maxChange) {
|
||||
maxChange = change;
|
||||
hot = event;
|
||||
}
|
||||
});
|
||||
return hot;
|
||||
}, [mainline.events]);
|
||||
|
||||
const hotEventId = hotEvent?.id;
|
||||
|
||||
// 当前显示的事件
|
||||
const displayedEvents = useMemo(() => {
|
||||
return mainline.events.slice(0, displayCount);
|
||||
@@ -114,6 +249,8 @@ const MainlineCard = React.memo(
|
||||
}}
|
||||
>
|
||||
{/* 卡片头部 */}
|
||||
<Box flexShrink={0}>
|
||||
{/* 第一行:概念名称 + 涨跌幅 + 事件数 */}
|
||||
<Flex
|
||||
align="center"
|
||||
justify="space-between"
|
||||
@@ -123,9 +260,6 @@ const MainlineCard = React.memo(
|
||||
onClick={onToggle}
|
||||
_hover={{ bg: COLORS.headerHoverBg }}
|
||||
transition="all 0.15s"
|
||||
borderBottomWidth="1px"
|
||||
borderBottomColor={COLORS.cardBorderColor}
|
||||
flexShrink={0}
|
||||
>
|
||||
<VStack align="start" spacing={0} flex={1} minW={0}>
|
||||
<HStack spacing={2} w="100%">
|
||||
@@ -181,10 +315,69 @@ const MainlineCard = React.memo(
|
||||
/>
|
||||
</Flex>
|
||||
|
||||
{/* HOT 事件展示区域 */}
|
||||
{hotEvent && (
|
||||
<Box
|
||||
px={3}
|
||||
py={2}
|
||||
bg="rgba(245, 101, 101, 0.08)"
|
||||
borderBottomWidth="1px"
|
||||
borderBottomColor={COLORS.cardBorderColor}
|
||||
cursor="pointer"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onEventSelect?.(hotEvent);
|
||||
}}
|
||||
_hover={{ bg: "rgba(245, 101, 101, 0.15)" }}
|
||||
transition="all 0.15s"
|
||||
>
|
||||
<HStack spacing={2} align="start">
|
||||
{/* HOT 标签 */}
|
||||
<Badge
|
||||
bg="linear-gradient(135deg, #f56565 0%, #ed8936 100%)"
|
||||
color="white"
|
||||
fontSize="10px"
|
||||
px={1.5}
|
||||
py={0.5}
|
||||
borderRadius="sm"
|
||||
flexShrink={0}
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
gap="2px"
|
||||
>
|
||||
<FireOutlined style={{ fontSize: 10 }} />
|
||||
HOT
|
||||
</Badge>
|
||||
{/* HOT 事件标题 */}
|
||||
<Text
|
||||
fontSize="xs"
|
||||
color={COLORS.textColor}
|
||||
noOfLines={2}
|
||||
flex={1}
|
||||
lineHeight="1.4"
|
||||
>
|
||||
{hotEvent.title}
|
||||
</Text>
|
||||
{/* HOT 事件涨幅 */}
|
||||
{(hotEvent.max_change_pct ?? hotEvent.change_pct) != null && (
|
||||
<Text
|
||||
fontSize="xs"
|
||||
fontWeight="bold"
|
||||
color="#fc8181"
|
||||
flexShrink={0}
|
||||
>
|
||||
+{(hotEvent.max_change_pct ?? hotEvent.change_pct).toFixed(1)}%
|
||||
</Text>
|
||||
)}
|
||||
</HStack>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{/* 事件列表区域 */}
|
||||
{isExpanded ? (
|
||||
<Box
|
||||
px={2}
|
||||
px={1}
|
||||
py={2}
|
||||
flex={1}
|
||||
overflowY="auto"
|
||||
@@ -199,14 +392,15 @@ const MainlineCard = React.memo(
|
||||
},
|
||||
}}
|
||||
>
|
||||
{/* 事件列表 - 单列布局 */}
|
||||
<VStack spacing={2} align="stretch">
|
||||
{/* 事件列表 - 带时间轴 */}
|
||||
<VStack spacing={0} align="stretch">
|
||||
{displayedEvents.map((event) => (
|
||||
<MiniEventCard
|
||||
<TimelineEventItem
|
||||
key={event.id}
|
||||
event={event}
|
||||
isSelected={selectedEvent?.id === event.id}
|
||||
onEventClick={onEventSelect}
|
||||
isHot={event.id === hotEventId}
|
||||
/>
|
||||
))}
|
||||
</VStack>
|
||||
@@ -229,27 +423,20 @@ const MainlineCard = React.memo(
|
||||
)}
|
||||
</Box>
|
||||
) : (
|
||||
/* 折叠时显示简要信息 */
|
||||
<Box px={3} py={2} flex={1} overflow="hidden">
|
||||
<VStack spacing={1} align="stretch">
|
||||
/* 折叠时显示简要信息 - 也带时间轴 */
|
||||
<Box px={1} py={2} flex={1} overflow="hidden">
|
||||
<VStack spacing={0} align="stretch">
|
||||
{mainline.events.slice(0, 4).map((event) => (
|
||||
<Text
|
||||
<TimelineEventItem
|
||||
key={event.id}
|
||||
fontSize="xs"
|
||||
color={COLORS.secondaryTextColor}
|
||||
noOfLines={1}
|
||||
cursor="pointer"
|
||||
_hover={{ color: COLORS.textColor }}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onEventSelect?.(event);
|
||||
}}
|
||||
>
|
||||
• {event.title}
|
||||
</Text>
|
||||
event={event}
|
||||
isSelected={selectedEvent?.id === event.id}
|
||||
onEventClick={onEventSelect}
|
||||
isHot={event.id === hotEventId}
|
||||
/>
|
||||
))}
|
||||
{mainline.events.length > 4 && (
|
||||
<Text fontSize="xs" color={COLORS.secondaryTextColor}>
|
||||
<Text fontSize="xs" color={COLORS.secondaryTextColor} pl="50px" pt={1}>
|
||||
... 还有 {mainline.events.length - 4} 条
|
||||
</Text>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user