web端聊天界面时间显示和气泡布局
This commit is contained in:
@@ -408,7 +408,7 @@ export const MessageBubble: React.FC<MessageBubbleProps> = ({
|
||||
)}
|
||||
|
||||
<View style={styles.messageContent}>
|
||||
{/* 群聊模式:显示发送者昵称 */}
|
||||
{/* 群聊模式:显示发送者昵称 - 放在气泡上方 */}
|
||||
{isGroupChat && !isMe && senderInfo && (
|
||||
<Text style={styles.senderName}>{senderInfo.nickname}</Text>
|
||||
)}
|
||||
|
||||
@@ -266,6 +266,43 @@ export const EmbeddedChat: React.FC<EmbeddedChatProps> = ({ conversation, onBack
|
||||
}
|
||||
};
|
||||
|
||||
// 格式化时间
|
||||
const formatTime = (dateString: string) => {
|
||||
const date = new Date(dateString);
|
||||
const now = new Date();
|
||||
const isToday = date.toDateString() === now.toDateString();
|
||||
|
||||
if (isToday) {
|
||||
return date.toLocaleTimeString('zh-CN', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
hour12: false
|
||||
});
|
||||
} else {
|
||||
return date.toLocaleString('zh-CN', {
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
hour12: false
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 判断是否需要显示时间(与上一条消息间隔超过5分钟)
|
||||
const shouldShowTime = (index: number) => {
|
||||
if (index === 0) return true;
|
||||
const currentMsg = messages[index];
|
||||
const prevMsg = messages[index - 1];
|
||||
if (!currentMsg || !prevMsg) return true;
|
||||
|
||||
const currentTime = new Date(currentMsg.created_at).getTime();
|
||||
const prevTime = new Date(prevMsg.created_at).getTime();
|
||||
const diffMinutes = (currentTime - prevTime) / (1000 * 60);
|
||||
|
||||
return diffMinutes >= 5;
|
||||
};
|
||||
|
||||
// 渲染消息内容
|
||||
const renderMessageContent = (item: MessageResponse) => {
|
||||
// 撤回消息
|
||||
@@ -356,8 +393,9 @@ export const EmbeddedChat: React.FC<EmbeddedChatProps> = ({ conversation, onBack
|
||||
};
|
||||
|
||||
// 渲染消息
|
||||
const renderMessage = ({ item }: { item: MessageResponse }) => {
|
||||
const renderMessage = ({ item, index }: { item: MessageResponse; index: number }) => {
|
||||
const isMe = String(item.sender_id) === String(currentUser?.id);
|
||||
const showTime = shouldShowTime(index);
|
||||
|
||||
// 处理指针事件(鼠标右键)- 使用 onPointerDown 检测鼠标右键
|
||||
const handlePointerDown = (e: any) => {
|
||||
@@ -372,31 +410,46 @@ export const EmbeddedChat: React.FC<EmbeddedChatProps> = ({ conversation, onBack
|
||||
};
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[styles.messageRow, isMe ? styles.messageRowRight : styles.messageRowLeft]}
|
||||
// @ts-ignore - React Native for Web 支持 pointer events
|
||||
onPointerDown={handlePointerDown}
|
||||
>
|
||||
{!isMe && (
|
||||
<Avatar
|
||||
source={item.sender?.avatar}
|
||||
size={36}
|
||||
name={item.sender?.nickname || item.sender?.username}
|
||||
/>
|
||||
<View>
|
||||
{/* 时间分隔 */}
|
||||
{showTime && (
|
||||
<View style={styles.timeContainer}>
|
||||
<Text style={styles.timeText}>{formatTime(item.created_at)}</Text>
|
||||
</View>
|
||||
)}
|
||||
<View style={[styles.messageBubble, isMe ? styles.messageBubbleMe : styles.messageBubbleOther]}>
|
||||
{isGroupChat && !isMe && (
|
||||
<Text style={styles.senderName}>{item.sender?.nickname || item.sender?.username}</Text>
|
||||
<View
|
||||
style={[styles.messageRow, isMe ? styles.messageRowRight : styles.messageRowLeft]}
|
||||
// @ts-ignore - React Native for Web 支持 pointer events
|
||||
onPointerDown={handlePointerDown}
|
||||
>
|
||||
{!isMe && (
|
||||
<View style={styles.avatarContainer}>
|
||||
<Avatar
|
||||
source={item.sender?.avatar}
|
||||
size={36}
|
||||
name={item.sender?.nickname || item.sender?.username}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
<View style={styles.messageContent}>
|
||||
{/* 群聊模式:显示发送者昵称 - 顶部与头像对齐 */}
|
||||
{isGroupChat && !isMe && (
|
||||
<Text style={styles.senderName}>{item.sender?.nickname || item.sender?.username}</Text>
|
||||
)}
|
||||
<View style={[styles.messageBubble, isMe ? styles.messageBubbleMe : styles.messageBubbleOther]}>
|
||||
{renderMessageContent(item)}
|
||||
</View>
|
||||
</View>
|
||||
{isMe && (
|
||||
<View style={styles.avatarContainer}>
|
||||
<Avatar
|
||||
source={currentUser?.avatar}
|
||||
size={36}
|
||||
name={currentUser?.nickname || currentUser?.username}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
{renderMessageContent(item)}
|
||||
</View>
|
||||
{isMe && (
|
||||
<Avatar
|
||||
source={currentUser?.avatar}
|
||||
size={36}
|
||||
name={currentUser?.nickname || currentUser?.username}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
@@ -664,9 +717,24 @@ const styles = StyleSheet.create({
|
||||
padding: spacing.md,
|
||||
paddingBottom: spacing.xl,
|
||||
},
|
||||
timeContainer: {
|
||||
alignItems: 'center',
|
||||
marginVertical: spacing.md,
|
||||
},
|
||||
timeText: {
|
||||
color: '#8E8E93',
|
||||
fontSize: 12,
|
||||
backgroundColor: 'rgba(142, 142, 147, 0.12)',
|
||||
paddingHorizontal: spacing.md,
|
||||
paddingVertical: 6,
|
||||
borderRadius: 14,
|
||||
fontWeight: '600',
|
||||
overflow: 'hidden',
|
||||
letterSpacing: 0.3,
|
||||
},
|
||||
messageRow: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'flex-end',
|
||||
alignItems: 'flex-start',
|
||||
marginBottom: spacing.md,
|
||||
maxWidth: screenWidth * 0.7,
|
||||
},
|
||||
@@ -677,6 +745,9 @@ const styles = StyleSheet.create({
|
||||
alignSelf: 'flex-end',
|
||||
justifyContent: 'flex-end',
|
||||
},
|
||||
avatarContainer: {
|
||||
marginTop: 0,
|
||||
},
|
||||
messageBubble: {
|
||||
maxWidth: screenWidth * 0.5,
|
||||
paddingHorizontal: spacing.md,
|
||||
@@ -693,10 +764,15 @@ const styles = StyleSheet.create({
|
||||
borderBottomLeftRadius: 4,
|
||||
...shadows.sm,
|
||||
},
|
||||
messageContent: {
|
||||
maxWidth: screenWidth * 0.5,
|
||||
alignItems: 'flex-start',
|
||||
},
|
||||
senderName: {
|
||||
fontSize: 12,
|
||||
color: '#999',
|
||||
marginBottom: 2,
|
||||
marginLeft: 4,
|
||||
},
|
||||
messageText: {
|
||||
fontSize: 15,
|
||||
|
||||
Reference in New Issue
Block a user