first commit
This commit is contained in:
117
app/components/Message.tsx
Normal file
117
app/components/Message.tsx
Normal file
@ -0,0 +1,117 @@
|
||||
import React, { useRef } from 'react';
|
||||
import { Box, Paper, Typography, Avatar, useMediaQuery, useTheme } from '@mui/material';
|
||||
import type { Message as MessageType } from '../types/types';
|
||||
|
||||
interface MessageProps {
|
||||
message: MessageType;
|
||||
isLast?: boolean;
|
||||
}
|
||||
|
||||
const Message: React.FC<MessageProps> = ({ message, isLast = false }) => {
|
||||
const isUser = message.sender === 'user';
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
|
||||
const paperRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// 计算气泡的borderRadius
|
||||
const borderRadius = isUser
|
||||
? '18px 18px 4px 18px'
|
||||
: '18px 18px 18px 4px';
|
||||
|
||||
return (
|
||||
<Box
|
||||
key={message.id}
|
||||
display="flex"
|
||||
marginBottom={2}
|
||||
justifyContent={isUser ? "flex-end" : "flex-start"}
|
||||
alignItems="flex-end"
|
||||
>
|
||||
<Box
|
||||
display="flex"
|
||||
alignItems="flex-end"
|
||||
maxWidth={isMobile ? "85%" : "70%"}
|
||||
gap={1.5}
|
||||
>
|
||||
{!isUser && (
|
||||
<Avatar
|
||||
sx={{
|
||||
width: 36,
|
||||
height: 36,
|
||||
bgcolor: theme.palette.primary.main,
|
||||
boxShadow: theme.shadows[2],
|
||||
transition: 'transform 0.2s',
|
||||
'&:hover': {
|
||||
transform: 'scale(1.05)',
|
||||
}
|
||||
}}
|
||||
>
|
||||
🤖
|
||||
</Avatar>
|
||||
)}
|
||||
|
||||
<Paper
|
||||
ref={paperRef}
|
||||
elevation={isLast ? 2 : 1}
|
||||
sx={{
|
||||
padding: 2,
|
||||
backgroundColor: isUser
|
||||
? theme.palette.primary.main
|
||||
: theme.palette.background.default,
|
||||
color: isUser ? 'white' : theme.palette.text.primary,
|
||||
borderRadius: borderRadius,
|
||||
position: 'relative',
|
||||
transition: 'all 0.3s ease',
|
||||
'&:hover': {
|
||||
boxShadow: theme.shadows[isLast ? 4 : 2],
|
||||
transform: 'translateY(-1px)',
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div style={{ lineHeight: 1.6 }}>
|
||||
{message.content.split('\n').map((line, index) => (
|
||||
<React.Fragment key={index}>
|
||||
{line}
|
||||
{index < message.content.split('\n').length - 1 && <br />}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<Typography
|
||||
variant="caption"
|
||||
sx={{
|
||||
display: 'block',
|
||||
marginTop: 1,
|
||||
opacity: 0.7,
|
||||
fontSize: '0.75rem',
|
||||
textAlign: 'right'
|
||||
}}
|
||||
>
|
||||
{message.timestamp.toLocaleTimeString([], {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
})}
|
||||
</Typography>
|
||||
</Paper>
|
||||
|
||||
{isUser && (
|
||||
<Avatar
|
||||
sx={{
|
||||
width: 36,
|
||||
height: 36,
|
||||
bgcolor: theme.palette.primary.dark,
|
||||
boxShadow: theme.shadows[2],
|
||||
transition: 'transform 0.2s',
|
||||
'&:hover': {
|
||||
transform: 'scale(1.05)',
|
||||
}
|
||||
}}
|
||||
>
|
||||
👤
|
||||
</Avatar>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Message;
|
||||
Reference in New Issue
Block a user