更新Company页面的UI为FUI风格

This commit is contained in:
2025-12-22 17:10:55 +08:00
parent 8936118133
commit 3ef1e6ea29

View File

@@ -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>
)}