Initial frontend repository commit.
Include app source and update .gitignore to exclude local release artifacts and signing files. Made-with: Cursor
This commit is contained in:
722
src/screens/message/GroupMembersScreen.tsx
Normal file
722
src/screens/message/GroupMembersScreen.tsx
Normal file
@@ -0,0 +1,722 @@
|
||||
/**
|
||||
* GroupMembersScreen 群成员管理界面
|
||||
* 显示群成员列表,支持管理员管理成员
|
||||
* 支持响应式网格布局
|
||||
*/
|
||||
|
||||
import React, { useState, useEffect, useCallback, useMemo } from 'react';
|
||||
import {
|
||||
View,
|
||||
StyleSheet,
|
||||
FlatList,
|
||||
TouchableOpacity,
|
||||
Alert,
|
||||
RefreshControl,
|
||||
ListRenderItem,
|
||||
Modal,
|
||||
TextInput,
|
||||
Dimensions,
|
||||
} from 'react-native';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { useNavigation, useRoute, RouteProp } from '@react-navigation/native';
|
||||
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
||||
import { colors, spacing, fontSizes, borderRadius } from '../../theme';
|
||||
import { useAuthStore } from '../../stores';
|
||||
import { groupService } from '../../services/groupService';
|
||||
import { groupManager } from '../../stores/groupManager';
|
||||
import { Avatar, Text, Button, Loading, EmptyState, Divider, ResponsiveContainer } from '../../components/common';
|
||||
import { useResponsive, useBreakpointGTE } from '../../hooks/useResponsive';
|
||||
import {
|
||||
GroupMemberResponse,
|
||||
GroupRole,
|
||||
} from '../../types/dto';
|
||||
import { RootStackParamList } from '../../navigation/types';
|
||||
|
||||
const { width: SCREEN_WIDTH } = Dimensions.get('window');
|
||||
|
||||
// 网格布局配置
|
||||
const GRID_CONFIG = {
|
||||
mobile: { columns: 1, itemWidth: '100%' },
|
||||
tablet: { columns: 2, itemWidth: '48%' },
|
||||
desktop: { columns: 3, itemWidth: '31%' },
|
||||
};
|
||||
|
||||
type NavigationProp = NativeStackNavigationProp<RootStackParamList>;
|
||||
type GroupMembersRouteProp = RouteProp<RootStackParamList, 'GroupMembers'>;
|
||||
|
||||
// 成员分组
|
||||
interface MemberGroup {
|
||||
title: string;
|
||||
data: GroupMemberResponse[];
|
||||
}
|
||||
|
||||
const GroupMembersScreen: React.FC = () => {
|
||||
const navigation = useNavigation<NavigationProp>();
|
||||
const route = useRoute<GroupMembersRouteProp>();
|
||||
const { groupId } = route.params;
|
||||
const { currentUser } = useAuthStore();
|
||||
|
||||
// 响应式布局
|
||||
const { isDesktop, isTablet, width } = useResponsive();
|
||||
const isWideScreen = useBreakpointGTE('lg');
|
||||
|
||||
// 计算网格列数
|
||||
const gridConfig = useMemo(() => {
|
||||
if (width >= 1024) return GRID_CONFIG.desktop;
|
||||
if (width >= 768) return GRID_CONFIG.tablet;
|
||||
return GRID_CONFIG.mobile;
|
||||
}, [width]);
|
||||
|
||||
// 成员列表状态
|
||||
const [members, setMembers] = useState<GroupMemberResponse[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
const [page, setPage] = useState(1);
|
||||
const [hasMore, setHasMore] = useState(true);
|
||||
|
||||
// 当前用户的成员信息
|
||||
const [currentMember, setCurrentMember] = useState<GroupMemberResponse | null>(null);
|
||||
|
||||
// 操作模态框状态
|
||||
const [actionModalVisible, setActionModalVisible] = useState(false);
|
||||
const [selectedMember, setSelectedMember] = useState<GroupMemberResponse | null>(null);
|
||||
const [actionLoading, setActionLoading] = useState(false);
|
||||
|
||||
// 设置群昵称模态框
|
||||
const [nicknameModalVisible, setNicknameModalVisible] = useState(false);
|
||||
const [newNickname, setNewNickname] = useState('');
|
||||
|
||||
// 计算当前用户的角色
|
||||
const isOwner = currentMember?.role === 'owner';
|
||||
const isAdmin = currentMember?.role === 'admin' || isOwner;
|
||||
|
||||
// 加载成员列表
|
||||
const loadMembers = useCallback(async (pageNum: number = 1, refresh: boolean = false) => {
|
||||
if (!hasMore && !refresh) return;
|
||||
|
||||
try {
|
||||
const response = await groupManager.getMembers(groupId, pageNum, 50);
|
||||
|
||||
if (refresh) {
|
||||
setMembers(response.list);
|
||||
setPage(1);
|
||||
} else {
|
||||
setMembers(prev => [...prev, ...response.list]);
|
||||
}
|
||||
|
||||
setHasMore(response.list.length === 50);
|
||||
|
||||
// 查找当前用户的成员信息
|
||||
const myMember = response.list.find(m => m.user_id === currentUser?.id);
|
||||
if (myMember) {
|
||||
setCurrentMember(myMember);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载成员列表失败:', error);
|
||||
}
|
||||
setLoading(false);
|
||||
setRefreshing(false);
|
||||
}, [groupId, currentUser, hasMore]);
|
||||
|
||||
// 初始加载
|
||||
useEffect(() => {
|
||||
loadMembers(1, true);
|
||||
}, [groupId]);
|
||||
|
||||
// 下拉刷新
|
||||
const onRefresh = useCallback(() => {
|
||||
setRefreshing(true);
|
||||
setHasMore(true);
|
||||
loadMembers(1, true);
|
||||
}, [loadMembers]);
|
||||
|
||||
// 加载更多
|
||||
const loadMore = useCallback(() => {
|
||||
if (!loading && hasMore) {
|
||||
const nextPage = page + 1;
|
||||
setPage(nextPage);
|
||||
loadMembers(nextPage);
|
||||
}
|
||||
}, [loading, hasMore, page, loadMembers]);
|
||||
|
||||
// 按角色分组
|
||||
const groupMembers = useCallback((): MemberGroup[] => {
|
||||
const owners = members.filter(m => m.role === 'owner');
|
||||
const admins = members.filter(m => m.role === 'admin');
|
||||
const normalMembers = members.filter(m => m.role === 'member');
|
||||
|
||||
const groups: MemberGroup[] = [];
|
||||
|
||||
if (owners.length > 0) {
|
||||
groups.push({ title: '群主', data: owners });
|
||||
}
|
||||
if (admins.length > 0) {
|
||||
groups.push({ title: '管理员', data: admins });
|
||||
}
|
||||
if (normalMembers.length > 0) {
|
||||
groups.push({ title: '成员', data: normalMembers });
|
||||
}
|
||||
|
||||
return groups;
|
||||
}, [members]);
|
||||
|
||||
// 打开操作菜单
|
||||
const openActionModal = (member: GroupMemberResponse) => {
|
||||
// 不能操作群主
|
||||
if (member.role === 'owner') return;
|
||||
|
||||
// 普通成员不能操作其他人
|
||||
if (!isAdmin) return;
|
||||
|
||||
// 管理员不能操作其他管理员(只有群主可以)
|
||||
if (!isOwner && member.role === 'admin') return;
|
||||
|
||||
setSelectedMember(member);
|
||||
setActionModalVisible(true);
|
||||
};
|
||||
|
||||
// 设置/取消管理员
|
||||
const handleToggleAdmin = async () => {
|
||||
if (!selectedMember) return;
|
||||
|
||||
const newRole: GroupRole = selectedMember.role === 'admin' ? 'member' : 'admin';
|
||||
const actionText = newRole === 'admin' ? '设为管理员' : '取消管理员';
|
||||
|
||||
Alert.alert(
|
||||
actionText,
|
||||
`确定要${actionText} "${selectedMember.user?.nickname || selectedMember.nickname}" 吗?`,
|
||||
[
|
||||
{ text: '取消', style: 'cancel' },
|
||||
{
|
||||
text: '确定',
|
||||
onPress: async () => {
|
||||
setActionLoading(true);
|
||||
try {
|
||||
await groupService.setMemberRole(groupId, selectedMember.user_id, {
|
||||
role: newRole as 'admin' | 'member',
|
||||
});
|
||||
|
||||
// 更新本地数据
|
||||
setMembers(prev => prev.map(m => {
|
||||
if (m.user_id === selectedMember.user_id) {
|
||||
return { ...m, role: newRole };
|
||||
}
|
||||
return m;
|
||||
}));
|
||||
|
||||
setActionModalVisible(false);
|
||||
Alert.alert('成功', `已${actionText}`);
|
||||
} catch (error: any) {
|
||||
console.error('设置角色失败:', error);
|
||||
Alert.alert('错误', error.message || '操作失败');
|
||||
} finally {
|
||||
setActionLoading(false);
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
);
|
||||
};
|
||||
|
||||
// 禁言/解禁成员
|
||||
const handleToggleMute = async () => {
|
||||
if (!selectedMember) return;
|
||||
|
||||
const newMuted = !selectedMember.muted;
|
||||
const actionText = newMuted ? '禁言' : '解禁';
|
||||
|
||||
Alert.alert(
|
||||
`${actionText}成员`,
|
||||
`确定要${actionText} "${selectedMember.user?.nickname || selectedMember.nickname}" 吗?`,
|
||||
[
|
||||
{ text: '取消', style: 'cancel' },
|
||||
{
|
||||
text: '确定',
|
||||
onPress: async () => {
|
||||
setActionLoading(true);
|
||||
try {
|
||||
// duration: 0 表示解除禁言,-1 表示永久禁言
|
||||
await groupService.muteMember(groupId, selectedMember.user_id, newMuted ? -1 : 0);
|
||||
|
||||
// 更新本地数据
|
||||
setMembers(prev => prev.map(m => {
|
||||
if (m.user_id === selectedMember.user_id) {
|
||||
return { ...m, muted: newMuted };
|
||||
}
|
||||
return m;
|
||||
}));
|
||||
|
||||
setActionModalVisible(false);
|
||||
Alert.alert('成功', `已${actionText}`);
|
||||
} catch (error: any) {
|
||||
console.error('禁言操作失败:', error);
|
||||
Alert.alert('错误', error.message || '操作失败');
|
||||
} finally {
|
||||
setActionLoading(false);
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
);
|
||||
};
|
||||
|
||||
// 移除成员
|
||||
const handleRemoveMember = () => {
|
||||
if (!selectedMember) return;
|
||||
|
||||
Alert.alert(
|
||||
'移除成员',
|
||||
`确定要将 "${selectedMember.user?.nickname || selectedMember.nickname}" 移出群聊吗?`,
|
||||
[
|
||||
{ text: '取消', style: 'cancel' },
|
||||
{
|
||||
text: '移除',
|
||||
style: 'destructive',
|
||||
onPress: async () => {
|
||||
setActionLoading(true);
|
||||
try {
|
||||
await groupService.removeMember(groupId, selectedMember.user_id);
|
||||
|
||||
// 更新本地数据
|
||||
setMembers(prev => prev.filter(m => m.user_id !== selectedMember.user_id));
|
||||
|
||||
setActionModalVisible(false);
|
||||
Alert.alert('成功', '已移除成员');
|
||||
} catch (error: any) {
|
||||
console.error('移除成员失败:', error);
|
||||
Alert.alert('错误', error.message || '操作失败');
|
||||
} finally {
|
||||
setActionLoading(false);
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
);
|
||||
};
|
||||
|
||||
// 打开设置群昵称模态框
|
||||
const openNicknameModal = (member: GroupMemberResponse) => {
|
||||
setSelectedMember(member);
|
||||
setNewNickname(member.nickname || member.user?.nickname || '');
|
||||
setNicknameModalVisible(true);
|
||||
};
|
||||
|
||||
// 设置群昵称(仅限自己)
|
||||
const handleSetNickname = async () => {
|
||||
if (!selectedMember || selectedMember.user_id !== currentUser?.id) return;
|
||||
|
||||
setActionLoading(true);
|
||||
try {
|
||||
await groupService.setNickname(groupId, {
|
||||
nickname: newNickname.trim() || selectedMember.user?.nickname || '',
|
||||
});
|
||||
|
||||
// 更新本地数据
|
||||
setMembers(prev => prev.map(m => {
|
||||
if (m.user_id === selectedMember.user_id) {
|
||||
return { ...m, nickname: newNickname.trim() };
|
||||
}
|
||||
return m;
|
||||
}));
|
||||
|
||||
setNicknameModalVisible(false);
|
||||
Alert.alert('成功', '群昵称已更新');
|
||||
} catch (error: any) {
|
||||
console.error('设置群昵称失败:', error);
|
||||
Alert.alert('错误', error.message || '操作失败');
|
||||
} finally {
|
||||
setActionLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 获取角色标签颜色
|
||||
const getRoleBadgeColor = (role: GroupRole): string => {
|
||||
switch (role) {
|
||||
case 'owner':
|
||||
return colors.warning.main;
|
||||
case 'admin':
|
||||
return colors.primary.main;
|
||||
default:
|
||||
return colors.text.hint;
|
||||
}
|
||||
};
|
||||
|
||||
// 获取角色标签文本
|
||||
const getRoleBadgeText = (role: GroupRole): string => {
|
||||
switch (role) {
|
||||
case 'owner':
|
||||
return '群主';
|
||||
case 'admin':
|
||||
return '管理员';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
// 渲染成员项
|
||||
const renderMember: ListRenderItem<GroupMemberResponse> = ({ item }) => {
|
||||
const isSelf = item.user_id === currentUser?.id;
|
||||
const canManage = isAdmin && item.role !== 'owner' && (isOwner || item.role === 'member');
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
style={styles.memberItem}
|
||||
onPress={() => canManage ? openActionModal(item) : null}
|
||||
onLongPress={() => isSelf ? openNicknameModal(item) : null}
|
||||
activeOpacity={canManage ? 0.7 : 1}
|
||||
>
|
||||
<Avatar
|
||||
source={item.user?.avatar}
|
||||
size={48}
|
||||
name={item.user?.nickname || item.nickname}
|
||||
/>
|
||||
<View style={styles.memberInfo}>
|
||||
<View style={styles.memberNameRow}>
|
||||
<Text variant="body" style={styles.memberName}>
|
||||
{item.nickname || item.user?.nickname || '未知用户'}
|
||||
</Text>
|
||||
{item.role !== 'member' && (
|
||||
<View style={[styles.roleBadge, { backgroundColor: getRoleBadgeColor(item.role) }]}>
|
||||
<Text variant="label" color={colors.background.paper}>
|
||||
{getRoleBadgeText(item.role)}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
<Text variant="caption" color={colors.text.secondary}>
|
||||
@{item.user?.username || 'unknown'}
|
||||
</Text>
|
||||
{item.muted && (
|
||||
<View style={styles.mutedBadge}>
|
||||
<MaterialCommunityIcons name="microphone-off" size={12} color={colors.error.main} />
|
||||
<Text variant="label" color={colors.error.main}> 禁言中</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
{canManage && (
|
||||
<MaterialCommunityIcons name="dots-vertical" size={20} color={colors.text.hint} />
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
// 渲染分组头部
|
||||
const renderSectionHeader = (title: string, count: number) => (
|
||||
<View style={styles.sectionHeader}>
|
||||
<Text variant="label" color={colors.text.secondary}>
|
||||
{title}
|
||||
</Text>
|
||||
<Text variant="caption" color={colors.text.hint}>
|
||||
{count}人
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
|
||||
// 渲染空状态
|
||||
const renderEmpty = () => {
|
||||
if (loading) return <Loading />;
|
||||
|
||||
return (
|
||||
<EmptyState
|
||||
title="暂无成员"
|
||||
description="群组还没有成员"
|
||||
icon="account-group-outline"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
// 渲染操作菜单
|
||||
const renderActionModal = () => {
|
||||
if (!selectedMember) return null;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
visible={actionModalVisible}
|
||||
animationType="slide"
|
||||
transparent
|
||||
onRequestClose={() => setActionModalVisible(false)}
|
||||
>
|
||||
<View style={styles.modalOverlay}>
|
||||
<View style={styles.modalContent}>
|
||||
<View style={styles.modalHeader}>
|
||||
<Avatar
|
||||
source={selectedMember.user?.avatar}
|
||||
size={60}
|
||||
name={selectedMember.user?.nickname || selectedMember.nickname}
|
||||
/>
|
||||
<Text variant="h3" style={styles.modalTitle}>
|
||||
{selectedMember.user?.nickname || selectedMember.nickname}
|
||||
</Text>
|
||||
<Text variant="caption" color={colors.text.secondary}>
|
||||
@{selectedMember.user?.username}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
<Divider />
|
||||
|
||||
{/* 设置/取消管理员(仅群主) */}
|
||||
{isOwner && selectedMember.role !== 'owner' && (
|
||||
<TouchableOpacity
|
||||
style={styles.actionItem}
|
||||
onPress={handleToggleAdmin}
|
||||
disabled={actionLoading}
|
||||
>
|
||||
<MaterialCommunityIcons
|
||||
name={selectedMember.role === 'admin' ? 'account-remove' : 'account-plus'}
|
||||
size={22}
|
||||
color={colors.text.primary}
|
||||
/>
|
||||
<Text variant="body" style={styles.actionText}>
|
||||
{selectedMember.role === 'admin' ? '取消管理员' : '设为管理员'}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
|
||||
{/* 禁言/解禁 */}
|
||||
<TouchableOpacity
|
||||
style={styles.actionItem}
|
||||
onPress={handleToggleMute}
|
||||
disabled={actionLoading}
|
||||
>
|
||||
<MaterialCommunityIcons
|
||||
name={selectedMember.muted ? 'microphone' : 'microphone-off'}
|
||||
size={22}
|
||||
color={selectedMember.muted ? colors.success.main : colors.error.main}
|
||||
/>
|
||||
<Text
|
||||
variant="body"
|
||||
color={selectedMember.muted ? colors.success.main : colors.error.main}
|
||||
style={styles.actionText}
|
||||
>
|
||||
{selectedMember.muted ? '解除禁言' : '禁言'}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
{/* 移除成员 */}
|
||||
<TouchableOpacity
|
||||
style={styles.actionItem}
|
||||
onPress={handleRemoveMember}
|
||||
disabled={actionLoading}
|
||||
>
|
||||
<MaterialCommunityIcons name="account-remove" size={22} color={colors.error.main} />
|
||||
<Text variant="body" color={colors.error.main} style={styles.actionText}>
|
||||
移除成员
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Button
|
||||
title="取消"
|
||||
variant="outline"
|
||||
onPress={() => setActionModalVisible(false)}
|
||||
fullWidth
|
||||
disabled={actionLoading}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
// 渲染设置群昵称模态框
|
||||
const renderNicknameModal = () => {
|
||||
if (!selectedMember) return null;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
visible={nicknameModalVisible}
|
||||
animationType="slide"
|
||||
transparent
|
||||
onRequestClose={() => setNicknameModalVisible(false)}
|
||||
>
|
||||
<View style={styles.modalOverlay}>
|
||||
<View style={styles.modalContent}>
|
||||
<Text variant="h3" style={styles.modalTitle}>设置群昵称</Text>
|
||||
|
||||
<Text variant="caption" color={colors.text.secondary} style={styles.inputLabel}>
|
||||
在本群的昵称(选填)
|
||||
</Text>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
value={newNickname}
|
||||
onChangeText={setNewNickname}
|
||||
placeholder={selectedMember.user?.nickname || '请输入群昵称'}
|
||||
placeholderTextColor={colors.text.hint}
|
||||
maxLength={20}
|
||||
/>
|
||||
|
||||
<View style={styles.modalButtons}>
|
||||
<Button
|
||||
title="取消"
|
||||
variant="outline"
|
||||
onPress={() => setNicknameModalVisible(false)}
|
||||
style={styles.modalButton}
|
||||
disabled={actionLoading}
|
||||
/>
|
||||
<Button
|
||||
title="保存"
|
||||
onPress={handleSetNickname}
|
||||
loading={actionLoading}
|
||||
style={styles.modalButton}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
// 渲染分组列表
|
||||
const renderGroupedList = () => {
|
||||
const groups = groupMembers();
|
||||
|
||||
if (groups.length === 0) {
|
||||
return renderEmpty();
|
||||
}
|
||||
|
||||
return (
|
||||
<FlatList
|
||||
data={groups}
|
||||
keyExtractor={(item) => item.title}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={onRefresh}
|
||||
colors={[colors.primary.main]}
|
||||
tintColor={colors.primary.main}
|
||||
/>
|
||||
}
|
||||
onEndReached={loadMore}
|
||||
onEndReachedThreshold={0.3}
|
||||
showsVerticalScrollIndicator={false}
|
||||
renderItem={({ item: group }) => (
|
||||
<View style={styles.section}>
|
||||
{renderSectionHeader(group.title, group.data.length)}
|
||||
{group.data.map((member, index) => (
|
||||
<View key={member.id}>
|
||||
{renderMember({ item: member, index } as any)}
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.container} edges={['bottom']}>
|
||||
{loading ? <Loading /> : renderGroupedList()}
|
||||
{renderActionModal()}
|
||||
{renderNicknameModal()}
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: colors.background.default,
|
||||
},
|
||||
section: {
|
||||
marginBottom: spacing.md,
|
||||
},
|
||||
sectionHeader: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
paddingHorizontal: spacing.md,
|
||||
paddingVertical: spacing.sm,
|
||||
backgroundColor: colors.background.default,
|
||||
},
|
||||
memberItem: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
paddingHorizontal: spacing.md,
|
||||
paddingVertical: spacing.md,
|
||||
backgroundColor: colors.background.paper,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: colors.divider,
|
||||
},
|
||||
memberInfo: {
|
||||
flex: 1,
|
||||
marginLeft: spacing.md,
|
||||
},
|
||||
memberNameRow: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginBottom: 2,
|
||||
},
|
||||
memberName: {
|
||||
fontWeight: '500',
|
||||
},
|
||||
roleBadge: {
|
||||
paddingHorizontal: spacing.xs,
|
||||
paddingVertical: 2,
|
||||
borderRadius: borderRadius.sm,
|
||||
marginLeft: spacing.sm,
|
||||
},
|
||||
mutedBadge: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginTop: 2,
|
||||
},
|
||||
// 模态框样式
|
||||
modalOverlay: {
|
||||
flex: 1,
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||
justifyContent: 'flex-end',
|
||||
},
|
||||
modalContent: {
|
||||
backgroundColor: colors.background.paper,
|
||||
borderTopLeftRadius: borderRadius.lg,
|
||||
borderTopRightRadius: borderRadius.lg,
|
||||
padding: spacing.lg,
|
||||
maxHeight: '80%',
|
||||
},
|
||||
modalHeader: {
|
||||
alignItems: 'center',
|
||||
marginBottom: spacing.md,
|
||||
},
|
||||
modalTitle: {
|
||||
fontWeight: '700',
|
||||
marginTop: spacing.sm,
|
||||
marginBottom: spacing.xs,
|
||||
},
|
||||
actionItem: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
paddingVertical: spacing.md,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: colors.divider,
|
||||
},
|
||||
actionText: {
|
||||
marginLeft: spacing.md,
|
||||
},
|
||||
inputLabel: {
|
||||
marginBottom: spacing.xs,
|
||||
},
|
||||
input: {
|
||||
backgroundColor: colors.background.default,
|
||||
borderRadius: borderRadius.md,
|
||||
paddingHorizontal: spacing.md,
|
||||
paddingVertical: spacing.md,
|
||||
fontSize: fontSizes.md,
|
||||
color: colors.text.primary,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.divider,
|
||||
marginBottom: spacing.md,
|
||||
},
|
||||
modalButtons: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
marginTop: spacing.md,
|
||||
},
|
||||
modalButton: {
|
||||
flex: 1,
|
||||
marginHorizontal: spacing.xs,
|
||||
},
|
||||
});
|
||||
|
||||
export default GroupMembersScreen;
|
||||
Reference in New Issue
Block a user