From 584d98307c6cabfda9fb2ca0a76cc001fbf9a857 Mon Sep 17 00:00:00 2001
From: lafay <2021211506@stu.hit.edu.cn>
Date: Sun, 29 Mar 2026 03:04:54 +0800
Subject: [PATCH] =?UTF-8?q?feat(ChatScreen):=20=E4=BC=98=E5=8C=96=E8=81=8A?=
=?UTF-8?q?=E5=A4=A9=E7=95=8C=E9=9D=A2=20UI=20=E5=92=8C=E4=BA=A4=E4=BA=92?=
=?UTF-8?q?=E4=BD=93=E9=AA=8C?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Made-with: Cursor
---
src/screens/message/ChatScreen.tsx | 1 +
src/screens/message/GroupMembersScreen.tsx | 41 ++++-
.../components/ChatScreen/ChatHeader.tsx | 21 ++-
.../components/ChatScreen/EmojiPanel.tsx | 84 ++++++---
.../components/ChatScreen/MorePanel.tsx | 71 ++++++--
.../components/ChatScreen/constants.ts | 50 ++++-
.../message/components/ChatScreen/styles.ts | 171 ++++++++++--------
.../message/components/ChatScreen/types.ts | 5 +-
8 files changed, 310 insertions(+), 134 deletions(-)
diff --git a/src/screens/message/ChatScreen.tsx b/src/screens/message/ChatScreen.tsx
index 09047a0..1e5f5ff 100644
--- a/src/screens/message/ChatScreen.tsx
+++ b/src/screens/message/ChatScreen.tsx
@@ -576,6 +576,7 @@ export const ChatScreen: React.FC = () => {
)}
diff --git a/src/screens/message/GroupMembersScreen.tsx b/src/screens/message/GroupMembersScreen.tsx
index a2a9624..8daf454 100644
--- a/src/screens/message/GroupMembersScreen.tsx
+++ b/src/screens/message/GroupMembersScreen.tsx
@@ -5,7 +5,7 @@
* 使用游标分页
*/
-import React, { useState, useEffect, useCallback, useMemo } from 'react';
+import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react';
import {
View,
StyleSheet,
@@ -19,7 +19,7 @@ import {
Dimensions,
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
-import { useLocalSearchParams } from 'expo-router';
+import { useLocalSearchParams, useRouter } from 'expo-router';
import { MaterialCommunityIcons } from '@expo/vector-icons';
import { spacing, fontSizes, borderRadius, useAppColors, type AppColors } from '../../theme';
import { useAuthStore } from '../../stores';
@@ -30,7 +30,7 @@ import {
IGroupMemberListPagedSource,
} from '../../stores/groupMemberListSources';
import { enrichGroupMembersWithUserProfiles } from '../../stores/groupMemberProfileResolver';
-import { Avatar, Text, Button, Loading, EmptyState, Divider, ResponsiveContainer } from '../../components/common';
+import { Avatar, Text, Button, Loading, EmptyState, Divider, ResponsiveContainer, AppBackButton } from '../../components/common';
import { useResponsive, useBreakpointGTE } from '../../hooks/useResponsive';
import { useCursorPagination } from '../../hooks/useCursorPagination';
import {
@@ -57,6 +57,7 @@ interface MemberGroup {
const GroupMembersScreen: React.FC = () => {
const colors = useAppColors();
const styles = useMemo(() => createGroupMembersStyles(colors), [colors]);
+ const router = useRouter();
const { groupId: groupIdParam } = useLocalSearchParams<{ groupId?: string | string[] }>();
const groupId = firstRouteParam(groupIdParam) ?? '';
const { currentUser } = useAuthStore();
@@ -149,11 +150,14 @@ const GroupMembersScreen: React.FC = () => {
setLoading(false);
}, [refresh]);
- // 初始加载
+ // 初始加载 - 使用 ref 避免依赖变化导致的无限循环
+ const initialLoadRef = React.useRef(false);
useEffect(() => {
- if (!groupId) return;
+ if (!groupId || initialLoadRef.current) return;
+ initialLoadRef.current = true;
refresh();
- }, [groupId, refresh]);
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [groupId]);
// 按角色分组
const groupMembers = useCallback((): MemberGroup[] => {
@@ -640,7 +644,13 @@ const GroupMembersScreen: React.FC = () => {
};
return (
-
+
+ {/* Header */}
+
+ router.back()} />
+ 群成员
+
+
{loading ? : renderGroupedList()}
{renderActionModal()}
{renderNicknameModal()}
@@ -654,6 +664,23 @@ function createGroupMembersStyles(colors: AppColors) {
flex: 1,
backgroundColor: colors.background.default,
},
+ // Header 样式
+ pageHeader: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'space-between',
+ paddingHorizontal: spacing.md,
+ paddingVertical: spacing.sm,
+ backgroundColor: colors.background.paper,
+ borderBottomWidth: 1,
+ borderBottomColor: colors.divider,
+ },
+ pageTitle: {
+ fontWeight: '600',
+ },
+ pageHeaderPlaceholder: {
+ width: 40,
+ },
section: {
marginBottom: spacing.md,
},
diff --git a/src/screens/message/components/ChatScreen/ChatHeader.tsx b/src/screens/message/components/ChatScreen/ChatHeader.tsx
index 6945dac..29fe64a 100644
--- a/src/screens/message/components/ChatScreen/ChatHeader.tsx
+++ b/src/screens/message/components/ChatScreen/ChatHeader.tsx
@@ -6,7 +6,7 @@
import React, { useMemo } from 'react';
import { View, TouchableOpacity, StyleSheet } from 'react-native';
import { MaterialCommunityIcons } from '@expo/vector-icons';
-import { Avatar, Text, AppBackButton } from '../../../../components/common';
+import { Avatar, Text } from '../../../../components/common';
import { useAppColors, spacing } from '../../../../theme';
import { useChatScreenStyles } from './styles';
import { useBreakpointGTE } from '../../../../hooks/useResponsive';
@@ -73,7 +73,9 @@ export const ChatHeader: React.FC = ({
return (
-
+
+
+
= ({
{isGroupChat ? (
<>
-
+
{getDisplayTitle()}
@@ -100,7 +107,7 @@ export const ChatHeader: React.FC = ({
<>
@@ -121,15 +128,17 @@ export const ChatHeader: React.FC = ({
-
+
) : (
-
+
)}
diff --git a/src/screens/message/components/ChatScreen/EmojiPanel.tsx b/src/screens/message/components/ChatScreen/EmojiPanel.tsx
index 0fd05c1..d206fe0 100644
--- a/src/screens/message/components/ChatScreen/EmojiPanel.tsx
+++ b/src/screens/message/components/ChatScreen/EmojiPanel.tsx
@@ -18,7 +18,7 @@ import { useAppColors, spacing } from '../../../../theme';
const { height: SCREEN_HEIGHT } = Dimensions.get('window');
- // 表情尺寸配置 - 固定宽度 40px,使用 flex 布局
+// 表情尺寸配置 - 固定宽度 40px,使用 flex 布局
const EMOJI_SIZES = {
mobile: { size: 24, itemWidth: 40, itemHeight: 40 },
tablet: { size: 26, itemWidth: 40, itemHeight: 40 },
@@ -234,19 +234,21 @@ export const EmojiPanel: React.FC = ({
);
};
- // 渲染管理按钮(表情面板第一位)
+ // 渲染管理按钮(表情面板第一位)- 现代扁平化设计
const renderManageButton = () => (
-
+
+
+
);
@@ -515,27 +517,30 @@ export const EmojiPanel: React.FC = ({
{activeTab === 'emoji' ? renderEmojiPanel() : renderStickerPanel()}
- {/* 底部 Tab 栏 */}
+ {/* 底部 Tab 栏 - 现代胶囊式设计 */}
- setActiveTab('emoji')}
- >
- 😊
-
- setActiveTab('sticker')}
- >
-
-
-
-
-
+
+ setActiveTab('emoji')}
+ activeOpacity={0.8}
+ >
+ 😊
+
+ setActiveTab('sticker')}
+ activeOpacity={0.8}
+ >
+
+
+
+
+
@@ -546,3 +551,26 @@ export const EmojiPanel: React.FC = ({
};
export default EmojiPanel;
+
+// 自定义表情按钮样式
+const stickerButtonStyles = StyleSheet.create({
+ manageButton: {
+ backgroundColor: '#F5F5F7',
+ borderRadius: 12,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ iconContainer: {
+ width: 44,
+ height: 44,
+ borderRadius: 12,
+ backgroundColor: '#FFFFFF',
+ alignItems: 'center',
+ justifyContent: 'center',
+ shadowColor: '#000000',
+ shadowOffset: { width: 0, height: 1 },
+ shadowOpacity: 0.06,
+ shadowRadius: 2,
+ elevation: 1,
+ },
+});
diff --git a/src/screens/message/components/ChatScreen/MorePanel.tsx b/src/screens/message/components/ChatScreen/MorePanel.tsx
index af957a2..f16f609 100644
--- a/src/screens/message/components/ChatScreen/MorePanel.tsx
+++ b/src/screens/message/components/ChatScreen/MorePanel.tsx
@@ -1,38 +1,81 @@
/**
- * 更多功能面板组件
+ * 更多功能面板组件 - 现代扁平化设计
*/
import React from 'react';
-import { View, TouchableOpacity } from 'react-native';
+import { View, TouchableOpacity, StyleSheet } from 'react-native';
import { MaterialCommunityIcons } from '@expo/vector-icons';
import { Text } from '../../../../components/common';
import { useChatScreenStyles } from './styles';
import { MORE_ACTIONS } from './constants';
import { MorePanelProps } from './types';
+// 渐变背景组件
+const GradientIcon: React.FC<{
+ colors: [string, string];
+ icon: string;
+ size?: number;
+}> = ({ colors, icon, size = 28 }) => {
+ return (
+
+
+
+
+ );
+};
+
+const gradientStyles = StyleSheet.create({
+ container: {
+ width: 56,
+ height: 56,
+ borderRadius: 18,
+ alignItems: 'center',
+ justifyContent: 'center',
+ overflow: 'hidden',
+ position: 'relative',
+ },
+ gradientOverlay: {
+ position: 'absolute',
+ bottom: 0,
+ left: 0,
+ right: 0,
+ height: '50%',
+ opacity: 0.3,
+ },
+});
+
export const MorePanel: React.FC = ({
onAction,
disabledActionIds = [],
+ isGroupChat = false,
}) => {
const styles = useChatScreenStyles();
const disabledSet = new Set(disabledActionIds);
+
+ // 群聊中隐藏语音通话和视频通话
+ const actions = isGroupChat
+ ? MORE_ACTIONS.filter(action => action.id !== 'voice_call' && action.id !== 'video_call')
+ : MORE_ACTIONS;
+
return (
- {MORE_ACTIONS.map((action) => {
+ {actions.map((action) => {
const disabled = disabledSet.has(action.id);
return (
- onAction(action.id)}
- disabled={disabled}
- >
-
-
-
- {action.name}
-
+ onAction(action.id)}
+ disabled={disabled}
+ activeOpacity={0.7}
+ >
+
+ {action.name}
+
);
})}
diff --git a/src/screens/message/components/ChatScreen/constants.ts b/src/screens/message/components/ChatScreen/constants.ts
index be77c7e..74de4d7 100644
--- a/src/screens/message/components/ChatScreen/constants.ts
+++ b/src/screens/message/components/ChatScreen/constants.ts
@@ -141,14 +141,50 @@ export const EMOJIS = [
'🌃', '🌌', '🌉', '🌫️', '🌊', '🕳️', '💊', '💉',
];
-// 更多功能项
+// 更多功能项 - 使用渐变色配置
export const MORE_ACTIONS: MoreAction[] = [
- { id: 'voice_call', icon: 'phone', name: '语音通话', color: '#4CAF50' },
- { id: 'video_call', icon: 'video', name: '视频通话', color: '#2196F3' },
- { id: 'image', icon: 'image', name: '图片', color: '#FF6B6B' },
- { id: 'camera', icon: 'camera', name: '拍摄', color: '#4ECDC4' },
- { id: 'file', icon: 'file-document', name: '文件', color: '#45B7D1' },
- { id: 'location', icon: 'map-marker', name: '位置', color: '#96CEB4' },
+ {
+ id: 'voice_call',
+ icon: 'phone',
+ name: '语音通话',
+ color: '#4CAF50',
+ gradientColors: ['#66BB6A', '#43A047'],
+ },
+ {
+ id: 'video_call',
+ icon: 'video',
+ name: '视频通话',
+ color: '#2196F3',
+ gradientColors: ['#42A5F5', '#1E88E5'],
+ },
+ {
+ id: 'image',
+ icon: 'image',
+ name: '图片',
+ color: '#FF6B35',
+ gradientColors: ['#FF8A50', '#F4511E'],
+ },
+ {
+ id: 'camera',
+ icon: 'camera',
+ name: '拍摄',
+ color: '#26A69A',
+ gradientColors: ['#4DB6AC', '#00897B'],
+ },
+ {
+ id: 'file',
+ icon: 'file-document',
+ name: '文件',
+ color: '#5C6BC0',
+ gradientColors: ['#7986CB', '#3F51B5'],
+ },
+ {
+ id: 'location',
+ icon: 'map-marker',
+ name: '位置',
+ color: '#EC407A',
+ gradientColors: ['#F06292', '#D81B60'],
+ },
];
// 消息撤回时间限制(毫秒)
diff --git a/src/screens/message/components/ChatScreen/styles.ts b/src/screens/message/components/ChatScreen/styles.ts
index ab60367..1aa7be5 100644
--- a/src/screens/message/components/ChatScreen/styles.ts
+++ b/src/screens/message/components/ChatScreen/styles.ts
@@ -30,13 +30,8 @@ export function createChatScreenStyles(colors: AppColors) {
// 头部
headerContainer: {
backgroundColor: colors.chat.card,
- borderBottomWidth: 1,
- borderBottomColor: 'rgba(255, 107, 53, 0.12)',
- shadowColor: colors.chat.shadow,
- shadowOffset: { width: 0, height: 3 },
- shadowOpacity: 0.06,
- shadowRadius: 10,
- elevation: 5,
+ borderBottomWidth: StyleSheet.hairlineWidth,
+ borderBottomColor: colors.chat.border,
},
header: {
flexDirection: 'row',
@@ -45,42 +40,47 @@ export function createChatScreenStyles(colors: AppColors) {
paddingHorizontal: spacing.md,
paddingVertical: spacing.sm + 2,
backgroundColor: colors.chat.card,
+ height: 56,
},
backButton: {
- width: 38,
- height: 38,
+ width: 36,
+ height: 36,
alignItems: 'center',
justifyContent: 'center',
- borderRadius: 19,
- backgroundColor: `${colors.primary.main}12`,
- borderWidth: 1,
- borderColor: `${colors.primary.main}26`,
+ borderRadius: 10,
+ backgroundColor: 'transparent',
},
headerCenter: {
- alignItems: 'stretch',
flex: 1,
+ alignItems: 'center',
marginHorizontal: spacing.sm,
},
titleRow: {
flexDirection: 'row',
alignItems: 'center',
- borderRadius: 14,
+ gap: spacing.sm,
paddingHorizontal: spacing.sm,
- paddingVertical: spacing.xs + 1,
- gap: spacing.xs + 2,
- flex: 1,
+ paddingVertical: spacing.xs,
+ borderRadius: 8,
},
titleLeading: {
- width: 34,
- height: 34,
- borderRadius: 17,
+ width: 36,
+ height: 36,
+ borderRadius: 10,
alignItems: 'center',
justifyContent: 'center',
- backgroundColor: `${colors.primary.main}14`,
+ backgroundColor: colors.chat.surfaceMuted,
+ overflow: 'hidden',
+ },
+ groupAvatar: {
+ width: 36,
+ height: 36,
+ borderRadius: 10,
},
titleContent: {
flex: 1,
minWidth: 0,
+ alignItems: 'center',
},
subtitleRow: {
flexDirection: 'row',
@@ -90,30 +90,27 @@ export function createChatScreenStyles(colors: AppColors) {
minHeight: 16,
},
headerName: {
- fontWeight: '700',
+ fontWeight: '600',
color: colors.text.primary,
- fontSize: 17,
+ fontSize: 16,
letterSpacing: -0.3,
},
memberCount: {
fontSize: 12,
color: colors.text.secondary,
- fontWeight: '500',
+ fontWeight: '400',
},
typingHint: {
fontSize: 12,
- color: colors.primary.dark,
- backgroundColor: `${colors.primary.main}14`,
- borderRadius: 10,
- paddingHorizontal: 6,
- paddingVertical: 1,
+ color: colors.primary.main,
+ fontWeight: '500',
},
moreButton: {
- width: 38,
- height: 38,
+ width: 36,
+ height: 36,
alignItems: 'center',
justifyContent: 'center',
- borderRadius: 19,
+ borderRadius: 10,
backgroundColor: 'transparent',
},
headerActions: {
@@ -550,55 +547,62 @@ export function createChatScreenStyles(colors: AppColors) {
borderTopWidth: 1,
borderTopColor: colors.chat.border,
},
- panelCloseButton: {
- width: 40,
- height: 40,
- alignItems: 'center',
- justifyContent: 'center',
- backgroundColor: colors.chat.card,
- borderRadius: 8,
- shadowColor: colors.chat.shadow,
- shadowOffset: { width: 0, height: 1 },
- shadowOpacity: 0.1,
- shadowRadius: 2,
- elevation: 2,
- },
// 面板内容区域
panelContent: {
flex: 1,
},
- // Tab 栏
+ // Tab 栏 - 现代胶囊式设计
panelTabBar: {
flexDirection: 'row',
alignItems: 'center',
- paddingHorizontal: spacing.sm,
- paddingVertical: spacing.xs,
- borderTopWidth: 1,
- borderTopColor: colors.chat.border,
+ justifyContent: 'space-between',
+ paddingHorizontal: spacing.md,
+ paddingVertical: spacing.sm + 2,
+ borderTopWidth: StyleSheet.hairlineWidth,
+ borderTopColor: colors.chat.borderHairline,
backgroundColor: colors.chat.card,
},
+ panelTabContainer: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ backgroundColor: colors.chat.surfaceMuted,
+ borderRadius: 12,
+ padding: 3,
+ },
panelTab: {
- width: 44,
- height: 44,
+ width: 42,
+ height: 36,
alignItems: 'center',
justifyContent: 'center',
- borderRadius: 8,
- marginRight: spacing.xs,
+ borderRadius: 10,
},
panelTabActive: {
- backgroundColor: colors.chat.replyTint,
+ backgroundColor: colors.chat.card,
+ shadowColor: colors.chat.shadow,
+ shadowOffset: { width: 0, height: 1 },
+ shadowOpacity: 0.08,
+ shadowRadius: 2,
+ elevation: 1,
},
panelTabEmoji: {
- fontSize: 24,
+ fontSize: 22,
},
panelTabDivider: {
width: 1,
- height: 24,
- backgroundColor: colors.chat.bubbleOutgoing,
+ height: 20,
+ backgroundColor: colors.chat.border,
marginHorizontal: spacing.sm,
},
+ panelCloseButton: {
+ width: 36,
+ height: 36,
+ alignItems: 'center',
+ justifyContent: 'center',
+ borderRadius: 10,
+ backgroundColor: colors.chat.surfaceMuted,
+ },
// 自定义表情面板
stickerLoadingContainer: {
@@ -643,35 +647,60 @@ export function createChatScreenStyles(colors: AppColors) {
height: '100%',
},
- // 更多功能面板
+ // 更多功能面板 - 现代扁平化设计
moreGrid: {
flexDirection: 'row',
flexWrap: 'wrap',
- padding: spacing.lg,
+ paddingHorizontal: spacing.lg + 4,
+ paddingTop: spacing.lg,
+ paddingBottom: spacing.xl,
+ justifyContent: 'flex-start',
},
moreItem: {
- width: (SCREEN_WIDTH - spacing.lg * 2) / 4,
+ width: (SCREEN_WIDTH - spacing.lg * 2 - 8) / 4,
alignItems: 'center',
- marginBottom: spacing.lg,
+ marginBottom: spacing.lg + 4,
},
moreIconContainer: {
- width: 60,
- height: 60,
- borderRadius: 16,
+ width: 56,
+ height: 56,
+ borderRadius: 18,
alignItems: 'center',
justifyContent: 'center',
- marginBottom: spacing.sm,
+ marginBottom: spacing.sm + 2,
backgroundColor: colors.chat.card,
shadowColor: colors.chat.shadow,
shadowOffset: { width: 0, height: 2 },
- shadowOpacity: 0.08,
- shadowRadius: 4,
+ shadowOpacity: 0.06,
+ shadowRadius: 6,
elevation: 2,
},
+ moreIconGradient: {
+ width: 56,
+ height: 56,
+ borderRadius: 18,
+ alignItems: 'center',
+ justifyContent: 'center',
+ marginBottom: spacing.sm + 2,
+ overflow: 'hidden',
+ },
+ moreIconInner: {
+ width: 56,
+ height: 56,
+ borderRadius: 18,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
moreItemText: {
- fontSize: 13,
- color: colors.chat.textTertiary,
+ fontSize: 12,
+ color: colors.chat.textSecondary,
fontWeight: '500',
+ letterSpacing: 0.3,
+ },
+ // MorePanel 整体背景
+ morePanelBackground: {
+ flex: 1,
+ backgroundColor: colors.chat.surfaceMuted,
},
// @成员选择浮层 - 绝对定位浮在输入框上方
diff --git a/src/screens/message/components/ChatScreen/types.ts b/src/screens/message/components/ChatScreen/types.ts
index 866f6bd..e803f02 100644
--- a/src/screens/message/components/ChatScreen/types.ts
+++ b/src/screens/message/components/ChatScreen/types.ts
@@ -48,6 +48,7 @@ export interface MoreAction {
icon: string;
name: string;
color: string;
+ gradientColors?: [string, string]; // 渐变色配置
}
// 长按菜单项
@@ -129,6 +130,8 @@ export interface EmojiPanelProps {
export interface MorePanelProps {
onAction: (actionId: string) => void;
disabledActionIds?: string[];
+ /** 是否为群聊(群聊中隐藏语音/视频通话) */
+ isGroupChat?: boolean;
}
// @成员选择面板 Props
@@ -168,7 +171,7 @@ export interface LongPressMenuProps {
// 聊天头部 Props
export interface ChatHeaderProps {
isGroupChat: boolean;
- groupInfo: { name?: string; member_count?: number } | null;
+ groupInfo: { name?: string; member_count?: number; avatar?: string | null } | null;
otherUser: { id?: string; nickname?: string; avatar?: string | null } | null;
routeGroupName?: string;
typingHint: string | null;