feat: 添加消息推送能力,添加新闻催化分析页的合规提示
This commit is contained in:
189
src/components/NotificationContainer/index.js
Normal file
189
src/components/NotificationContainer/index.js
Normal file
@@ -0,0 +1,189 @@
|
||||
// src/components/NotificationContainer/index.js
|
||||
/**
|
||||
* 通知容器组件 - 右下角层叠显示实时通知
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
Box,
|
||||
VStack,
|
||||
HStack,
|
||||
Text,
|
||||
IconButton,
|
||||
Icon,
|
||||
useColorModeValue,
|
||||
Slide,
|
||||
ScaleFade,
|
||||
} from '@chakra-ui/react';
|
||||
import { MdClose, MdCheckCircle, MdError, MdWarning, MdInfo } from 'react-icons/md';
|
||||
import { useNotification } from '../../contexts/NotificationContext';
|
||||
|
||||
// 通知类型对应的图标和颜色
|
||||
const NOTIFICATION_STYLES = {
|
||||
success: {
|
||||
icon: MdCheckCircle,
|
||||
colorScheme: 'green',
|
||||
bg: 'green.50',
|
||||
borderColor: 'green.400',
|
||||
iconColor: 'green.500',
|
||||
},
|
||||
error: {
|
||||
icon: MdError,
|
||||
colorScheme: 'red',
|
||||
bg: 'red.50',
|
||||
borderColor: 'red.400',
|
||||
iconColor: 'red.500',
|
||||
},
|
||||
warning: {
|
||||
icon: MdWarning,
|
||||
colorScheme: 'orange',
|
||||
bg: 'orange.50',
|
||||
borderColor: 'orange.400',
|
||||
iconColor: 'orange.500',
|
||||
},
|
||||
info: {
|
||||
icon: MdInfo,
|
||||
colorScheme: 'blue',
|
||||
bg: 'blue.50',
|
||||
borderColor: 'blue.400',
|
||||
iconColor: 'blue.500',
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* 单个通知项组件
|
||||
*/
|
||||
const NotificationItem = ({ notification, onClose, isNewest = false }) => {
|
||||
const { id, severity = 'info', title, message } = notification;
|
||||
const style = NOTIFICATION_STYLES[severity] || NOTIFICATION_STYLES.info;
|
||||
|
||||
const bgColor = useColorModeValue(style.bg, `${style.colorScheme}.900`);
|
||||
const borderColor = useColorModeValue(style.borderColor, `${style.colorScheme}.500`);
|
||||
const textColor = useColorModeValue('gray.800', 'white');
|
||||
const subTextColor = useColorModeValue('gray.600', 'gray.300');
|
||||
|
||||
return (
|
||||
<ScaleFade initialScale={0.9} in={true}>
|
||||
<Box
|
||||
bg={bgColor}
|
||||
borderLeft="4px solid"
|
||||
borderColor={borderColor}
|
||||
borderRadius="md"
|
||||
boxShadow={isNewest ? '2xl' : 'lg'} // 最新消息更强的阴影
|
||||
p={4}
|
||||
minW="350px"
|
||||
maxW="450px"
|
||||
position="relative"
|
||||
_hover={{
|
||||
boxShadow: 'xl',
|
||||
transform: 'translateX(-4px)',
|
||||
}}
|
||||
transition="all 0.2s"
|
||||
// 最新消息添加微妙的高亮边框
|
||||
{...(isNewest && {
|
||||
borderRight: '1px solid',
|
||||
borderRightColor: borderColor,
|
||||
borderTop: '1px solid',
|
||||
borderTopColor: useColorModeValue(`${style.colorScheme}.100`, `${style.colorScheme}.700`),
|
||||
})}
|
||||
>
|
||||
<HStack spacing={3} align="start">
|
||||
{/* 图标 */}
|
||||
<Icon
|
||||
as={style.icon}
|
||||
w={6}
|
||||
h={6}
|
||||
color={style.iconColor}
|
||||
mt={0.5}
|
||||
flexShrink={0}
|
||||
/>
|
||||
|
||||
{/* 内容 */}
|
||||
<VStack align="start" spacing={1} flex={1} mr={6}>
|
||||
<Text
|
||||
fontSize="md"
|
||||
fontWeight="bold"
|
||||
color={textColor}
|
||||
lineHeight="short"
|
||||
>
|
||||
{title}
|
||||
</Text>
|
||||
{message && (
|
||||
<Text
|
||||
fontSize="sm"
|
||||
color={subTextColor}
|
||||
lineHeight="short"
|
||||
>
|
||||
{message}
|
||||
</Text>
|
||||
)}
|
||||
</VStack>
|
||||
|
||||
{/* 关闭按钮 */}
|
||||
<IconButton
|
||||
icon={<MdClose />}
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
colorScheme={style.colorScheme}
|
||||
aria-label="关闭通知"
|
||||
onClick={() => onClose(id)}
|
||||
position="absolute"
|
||||
top={2}
|
||||
right={2}
|
||||
_hover={{
|
||||
bg: useColorModeValue(`${style.colorScheme}.100`, `${style.colorScheme}.800`),
|
||||
}}
|
||||
/>
|
||||
</HStack>
|
||||
</Box>
|
||||
</ScaleFade>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* 通知容器组件 - 主组件
|
||||
*/
|
||||
const NotificationContainer = () => {
|
||||
const { notifications, removeNotification } = useNotification();
|
||||
|
||||
// 如果没有通知,不渲染
|
||||
if (notifications.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Box
|
||||
position="fixed"
|
||||
bottom={6}
|
||||
right={6}
|
||||
zIndex={9999}
|
||||
pointerEvents="none"
|
||||
>
|
||||
<VStack
|
||||
spacing={3} // 消息之间间距 12px
|
||||
align="flex-end"
|
||||
pointerEvents="auto"
|
||||
>
|
||||
{notifications.map((notification, index) => (
|
||||
<Slide
|
||||
key={notification.id}
|
||||
direction="right"
|
||||
in={true}
|
||||
style={{
|
||||
position: 'relative',
|
||||
zIndex: 9999 - index, // 最新消息(index=0)z-index最高
|
||||
}}
|
||||
>
|
||||
<NotificationItem
|
||||
notification={notification}
|
||||
onClose={removeNotification}
|
||||
isNewest={index === 0} // 第一条消息是最新的
|
||||
/>
|
||||
</Slide>
|
||||
))}
|
||||
</VStack>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default NotificationContainer;
|
||||
Reference in New Issue
Block a user