概览

  • 页面以“群组列表 → 创建群组模态 → 群组管理模态”构成,数据源分为 groups 与 members 两块,selectedGroup 与可见状态控制模态显隐。操作涵盖增删改群组、成员增删与角色切换,采用不可变更新,结构清晰。
  • iOS/Android 走 RN 标准组件栈;在鸿蒙(OpenHarmony)落地,重点在列表虚拟化、弹窗层级与返回手势、输入法合成事件(IME)、窗口尺寸适配与图标一致性。

核心组件分工

  • GroupCard 展示群组基础信息(名称、描述、成员数、管理员、时间)与三项操作(编辑、删除、管理)。点击“管理”进入群组管理模态页,职责单一,便于复用。
  • MemberItem 承载成员项展示与角色切换/移除操作。角色切换通过本地轮换(admin → moderator → member)触发 onRoleChange 上抛事件,移除触发 onRemove;这把业务动作从展示层抽离,有利于后续接入真实后端或权限校验。
  • CreateGroupModal 以条件渲染实现自定义模态,收集群组名称、描述与是否私密,提交上抛最小必要数据,由页面层补全 id/members/admin/createdAt。
  • GroupManagementPage 是管理模态的主视图,包含信息区(查看/编辑群组信息)与成员区(添加成员、列表展示及按项操作)。editMode 切换编辑表单与展示视图,editedGroup 状态保持正在编辑的群组副本,避免直接改动原数据。

数据与状态流

  • groups 初始化包含多群组,members 为当前选定群组的成员集合(该示例为全局成员集合,生产应按群组维度拉取)。selectedGroup 控制管理页当前对象;showCreateModal/showManagementModal 控制模态显隐。
  • 创建群组 handleCreateGroup 接收简化表单数据,页面层补齐剩余字段(id/members/admin/createdAt)并追加到列表。删除群组 handleDeleteGroup 用 Alert 二次确认后过滤移除。管理入口 handleManageGroup 设置 selectedGroup 并打开管理模态。
  • 群组更新 handleUpdateGroup 用不可变更新替换目标项,同时更新 selectedGroup;添加成员 handleAddMember 追加成员并增加当前群组成员计数;移除成员 handleRemoveMember 过滤成员并减少当前群组成员计数,保持页面状态一致性。

列表与虚拟化

  • 群组列表使用 FlatList 渲染,keyExtractor 以稳定 id 保证 diff 正确;ListHeaderComponent 展示总数,结构直观。
  • 性能建议:为 GroupCard 使用 React.memo,renderItem 用 useCallback 缓存,避免频繁重渲染;若卡片高度可预测,增加 getItemLayout 提升滚动与定位稳定性。
  • 鸿蒙适配:FlatList 的滚动物理(回弹/阻尼/惯性)与事件窗口需在适配层映射到 ArkUI 滚动控件,确保 scrollToIndex、onEndReached 等行为一致。

模态与叠层行为

  • 两个模态均采用条件渲染的覆盖层(绝对定位半透明背景)模拟弹窗,原型阶段简洁;生产建议改用 RN Modal 或 Portal 以获得更稳定的系统层级、返回手势关闭与可访问性焦点迁移。鸿蒙端需桥接 ArkUI 弹窗能力(层级、焦点、手势),避免遮罩与返回行为不一致。
  • 关闭逻辑统一:CreateGroupModal 提交后清理表单与关闭,GroupManagementPage 在保存后重置 editMode;建议显式清理临时状态(例如 editedGroup 与 newMemberName)以防状态泄漏。

输入法与表单治理

  • TextInput 在中文 IME 下的合成事件(composition)与选择范围(selection)在三端存在差异;鸿蒙端需在适配层正确映射到 RN 的 onChangeText/onSelectionChange,避免“中间态”被误判或光标错乱。
  • 表单校验最小化(仅群组名称必填),生产建议增加更完整的校验(最小长度、字符集、私密群组策略说明)与错误提示统一处理。

角色治理与权限

  • MemberItem 的角色切换仅为 UI 演示;真实场景应在服务层校验当前用户权限(仅管理员/版主可变更)、避免给自己降级/删除等非法操作,并在失败时回滚 UI 状态。
  • 角色文本映射在视图层直出(管理员/版主/普通成员),建议抽象到字典,方便 i18n 与统一治理。

图标与视觉一致性

  • 图标采用 emoji(👥、✏️、🗑️ 等),原型友好但在不同系统字体下易出现基线与色彩差异;生产建议迁移到统一矢量/字体图标栈,并在鸿蒙端通过 ArkUI 渲染能力保持像素与对齐一致。
  • 颜色与字号在三端可能有细微表现差异,建议通过主题系统集中管理,避免视图层散落定义。

窗口与尺寸适配

  • 本页多处卡片与气泡宽度受屏幕宽度影响;使用 Dimensions.get(‘window’) 的初始值在横竖屏或分屏场景会失效。建议改用 useWindowDimensions 并监听窗口变化,鸿蒙端需确保窗口事件能正确传递到 RN 层,保证布局稳定。

状态更新一致性

  • 多数更新使用闭包中的 groups/members;高并发交互下建议改为函数式更新(setGroups(prev => …),setMembers(prev => …)),避免旧值导致竞态;特别是“添加成员同时更新群组成员数”的组合更新,更适合函数式写法。
  • 模态之间的状态耦合尽量降低,selectedGroup 的变更与 members 的变更同步后再触发视图重渲染,减少闪烁与不一致。

概述

本文分析的是一个基于React Native构建的群组管理应用,集成了群组创建、成员管理、权限控制等核心功能。该应用采用了多层级的模态框设计、复杂的状态联动和精细的权限管理机制,展现了社交管理类应用的典型技术架构。在鸿蒙OS的跨端适配场景中,这种涉及多层级交互和复杂状态管理的应用具有重要的技术参考价值。

核心架构设计深度解析

多层级模态框交互系统

应用实现了三级模态交互体系:

// 1. 创建群组模态框
const CreateGroupModal = ({ visible, onClose, onCreate }) => {
  const [name, setName] = useState('');
  // ...其他状态
  
  return visible ? (
    <View style={styles.modalOverlay}>
      <View style={styles.modalContent}>
        {/* 表单内容 */}
      </View>
    </View>
  ) : null;
};

// 2. 群组管理页面模态框
const GroupManagementPage = ({ group, members, visible, onClose }) => {
  const [editMode, setEditMode] = useState(false);
  // ...其他状态
  
  return visible && group ? (
    <View style={styles.managementModalOverlay}>
      <View style={styles.managementModalContent}>
        {/* 管理界面内容 */}
      </View>
    </View>
  ) : null;
};

// 3. 主页面使用
<>
  <CreateGroupModal 
    visible={showCreateModal} 
    onClose={() => setShowCreateModal(false)}
    onCreate={handleCreateGroup}
  />
  <GroupManagementPage
    group={selectedGroup}
    members={members}
    visible={showManagementModal}
    onClose={() => setShowManagementModal(false)}
  />
</>

这种设计采用了条件渲染技术,通过visible属性控制模态框的显示/隐藏。三级模态体系形成了清晰的功能分层:创建群组→管理群组→成员管理。zIndex属性确保了模态框的正确叠加顺序。

在鸿蒙ArkUI体系中,模态框需要完全重构:

@Component
struct CreateGroupDialog {
  @Prop visible: boolean = false;
  @State name: string = '';
  @Event onClose: () => void;
  @Event onCreate: (name: string) => void;
  
  build() {
    if (this.visible) {
      Column() {
        // 对话框内容
        TextInput({ text: this.name })
        Button('创建', { type: ButtonType.Normal })
          .onClick(() => {
            this.onCreate(this.name);
            this.onClose();
          })
      }
      .zIndex(100)
    }
  }
}

复杂状态联动机制

应用实现了群组与成员的状态联动:

const handleAddMember = (name: string) => {
  const newMember = {
    id: `m${Date.now()}`,
    name,
    role: 'member',
    joinedAt: new Date().toLocaleDateString('zh-CN')
  };
  
  setMembers([...members, newMember]);
  
  // 更新关联群组成员数
  if (selectedGroup) {
    const updatedGroup = { ...selectedGroup, members: selectedGroup.members + 1 };
    setGroups(groups.map(g => g.id === selectedGroup.id ? updatedGroup : g));
    setSelectedGroup(updatedGroup);
  }
};

这种设计体现了状态管理的核心原则:保持数据一致性。当添加新成员时,同时更新成员列表和关联群组的成员计数。展开运算符和map操作确保了不可变数据更新。

鸿蒙的实现需要适配其响应式系统:

@State groups: Group[] = [];
@State members: GroupMember[] = [];
@State selectedGroup: Group | null = null;

handleAddMember(name: string) {
  const newMember: GroupMember = {
    id: `m${Date.now()}`,
    name,
    role: 'member',
    joinedAt: new Date().toLocaleDateString('zh-CN')
  };
  
  this.members = [...this.members, newMember];
  
  if (this.selectedGroup) {
    this.groups = this.groups.map(g => 
      g.id === this.selectedGroup.id 
        ? { ...g, members: g.members + 1 } 
        : g
    );
    this.selectedGroup = { ...this.selectedGroup, members: this.selectedGroup.members + 1 };
  }
}

角色权限管理系统

MemberItem组件实现了动态角色切换:

const MemberItem = ({ member, onRoleChange }) => {
  return (
    <View style={styles.memberItem}>
      {/* 成员信息 */}
      <TouchableOpacity 
        onPress={() => {
          const roles: Array<'admin' | 'moderator' | 'member'> = ['admin', 'moderator', 'member'];
          const currentIndex = roles.indexOf(member.role);
          const nextIndex = (currentIndex + 1) % roles.length;
          onRoleChange(member.id, roles[nextIndex]);
        }}
      >
        <Text>更改角色</Text>
      </TouchableOpacity>
    </View>
  );
};

这种设计通过角色数组和模运算实现了角色循环切换,避免了复杂的条件判断。明确的类型定义确保了角色值的类型安全。

鸿蒙的角色切换实现:

@Component
struct MemberItem {
  @Prop member: GroupMember;
  @Event onRoleChange: (id: string, role: 'admin' | 'moderator' | 'member') => void;
  
  roles: Array<'admin' | 'moderator' | 'member'> = ['admin', 'moderator', 'member'];
  
  changeRole() {
    const currentIndex = this.roles.indexOf(this.member.role);
    const nextIndex = (currentIndex + 1) % this.roles.length;
    this.onRoleChange(this.member.id, this.roles[nextIndex]);
  }
  
  build() {
    Button('更改角色', { type: ButtonType.Normal })
      .onClick(() => this.changeRole())
  }
}

跨端适配技术方案

组件映射策略

React Native组件 鸿蒙ArkUI组件 关键适配点
Modal-like View CustomDialog 模态交互实现差异
FlatList List 列表实现和性能优化
TouchableOpacity Button 交互反馈机制不同
ScrollView Scroll 滚动行为一致

样式系统转换

// React Native
memberItem: {
  flexDirection: 'row',
  justifyContent: 'space-between',
  paddingVertical: 12,
  borderBottomWidth: 1,
  borderBottomColor: '#e2e8f0',
},

// 鸿蒙
Row() {
  // 成员信息
}
.width('100%')
.padding({ top: 12, bottom: 12 })
.border({ width: 1, color: '#e2e8f0' })

状态管理迁移

// React Native
const [groups, setGroups] = useState<Group[]>([]);
const [members, setMembers] = useState<GroupMember[]>([]);

// 鸿蒙
@State groups: Group[] = [];
@State members: GroupMember[] = [];

性能优化与最佳实践

列表渲染优化

配置keyExtractor提升列表性能:

<FlatList
  data={members}
  keyExtractor={item => item.id}
  renderItem={({ item }) => <MemberItem member={item} />}
/>

状态更新优化

使用函数式更新避免依赖当前状态:

setGroups(prevGroups => 
  prevGroups.map(g => 
    g.id === selectedGroup.id 
      ? { ...g, members: g.members + 1 } 
      : g
  )
);

完整代码code:

// app.tsx
import React, { useState } from 'react';
import { SafeAreaView, View, Text, StyleSheet, TouchableOpacity, ScrollView, Dimensions, Alert, TextInput, FlatList } from 'react-native';

// 图标库
const ICONS = {
  group: '👥',
  add: '➕',
  edit: '✏️',
  delete: '🗑️',
  member: '👤',
  settings: '⚙️',
  leave: '🚪',
  search: '🔍',
};

const { width } = Dimensions.get('window');

// 群组类型
type Group = {
  id: string;
  name: string;
  description: string;
  members: number;
  admin: string;
  isPrivate: boolean;
  createdAt: string;
};

// 群成员类型
type GroupMember = {
  id: string;
  name: string;
  role: 'admin' | 'moderator' | 'member';
  joinedAt: string;
};

// 群组卡片组件
const GroupCard = ({ 
  group, 
  onEdit, 
  onDelete,
  onManage
}: { 
  group: Group; 
  onEdit: (group: Group) => void;
  onDelete: (id: string) => void;
  onManage: (group: Group) => void;
}) => {
  return (
    <View style={styles.groupCard}>
      <View style={styles.groupHeader}>
        <View style={styles.groupIcon}>
          <Text style={styles.groupIconText}>{ICONS.group}</Text>
        </View>
        <View style={styles.groupInfo}>
          <Text style={styles.groupName}>{group.name}</Text>
          <Text style={styles.groupDescription} numberOfLines={1}>{group.description}</Text>
        </View>
      </View>
      
      <View style={styles.groupDetails}>
        <View style={styles.detailItem}>
          <Text style={styles.detailIcon}>{ICONS.member}</Text>
          <Text style={styles.detailText}>{group.members} 成员</Text>
        </View>
        <View style={styles.detailItem}>
          <Text style={styles.detailText}>{group.admin}</Text>
        </View>
        <View style={styles.detailItem}>
          <Text style={styles.detailText}>{group.createdAt}</Text>
        </View>
      </View>
      
      <View style={styles.groupActions}>
        <TouchableOpacity style={styles.actionButton} onPress={() => onEdit(group)}>
          <Text style={styles.actionIcon}>{ICONS.edit}</Text>
          <Text style={styles.actionText}>编辑</Text>
        </TouchableOpacity>
        <TouchableOpacity style={styles.actionButton} onPress={() => onDelete(group.id)}>
          <Text style={styles.actionIcon}>{ICONS.delete}</Text>
          <Text style={styles.actionText}>删除</Text>
        </TouchableOpacity>
        <TouchableOpacity style={styles.manageButton} onPress={() => onManage(group)}>
          <Text style={styles.manageText}>管理</Text>
        </TouchableOpacity>
      </View>
    </View>
  );
};

// 群成员项组件
const MemberItem = ({ 
  member, 
  onRoleChange,
  onRemove
}: { 
  member: GroupMember; 
  onRoleChange: (id: string, role: 'admin' | 'moderator' | 'member') => void;
  onRemove: (id: string) => void;
}) => {
  return (
    <View style={styles.memberItem}>
      <View style={styles.memberInfo}>
        <Text style={styles.memberAvatar}>{ICONS.member}</Text>
        <View>
          <Text style={styles.memberName}>{member.name}</Text>
          <Text style={styles.memberRole}>{member.role === 'admin' ? '管理员' : member.role === 'moderator' ? '版主' : '普通成员'}</Text>
        </View>
      </View>
      
      <View style={styles.memberActions}>
        <TouchableOpacity 
          style={styles.roleButton} 
          onPress={() => {
            const roles: Array<'admin' | 'moderator' | 'member'> = ['admin', 'moderator', 'member'];
            const currentIndex = roles.indexOf(member.role);
            const nextIndex = (currentIndex + 1) % roles.length;
            onRoleChange(member.id, roles[nextIndex]);
          }}
        >
          <Text style={styles.roleText}>更改角色</Text>
        </TouchableOpacity>
        <TouchableOpacity 
          style={styles.removeButton} 
          onPress={() => onRemove(member.id)}
        >
          <Text style={styles.removeText}>移除</Text>
        </TouchableOpacity>
      </View>
    </View>
  );
};

// 创建群组模态框
const CreateGroupModal = ({ 
  visible, 
  onClose, 
  onCreate 
}: { 
  visible: boolean; 
  onClose: () => void; 
  onCreate: (groupData: Omit<Group, 'id' | 'members' | 'admin' | 'createdAt'>) => void 
}) => {
  const [name, setName] = useState('');
  const [description, setDescription] = useState('');
  const [isPrivate, setIsPrivate] = useState(false);

  const handleSubmit = () => {
    if (!name.trim()) {
      Alert.alert('错误', '请输入群组名称');
      return;
    }

    onCreate({
      name: name.trim(),
      description: description.trim(),
      isPrivate
    });

    setName('');
    setDescription('');
    setIsPrivate(false);
    onClose();
  };

  if (!visible) return null;

  return (
    <View style={styles.modalOverlay}>
      <View style={styles.modalContent}>
        <Text style={styles.modalTitle}>创建新群组</Text>
        
        <View style={styles.inputGroup}>
          <Text style={styles.inputLabel}>群组名称 *</Text>
          <TextInput
            style={styles.input}
            value={name}
            onChangeText={setName}
            placeholder="请输入群组名称"
          />
        </View>
        
        <View style={styles.inputGroup}>
          <Text style={styles.inputLabel}>群组描述</Text>
          <TextInput
            style={[styles.input, styles.textArea]}
            value={description}
            onChangeText={setDescription}
            placeholder="描述群组的目的和规则"
            multiline
            numberOfLines={3}
          />
        </View>
        
        <View style={styles.checkboxGroup}>
          <TouchableOpacity 
            style={styles.checkbox} 
            onPress={() => setIsPrivate(!isPrivate)}
          >
            <Text style={styles.checkboxText}>{isPrivate ? '🔒' : '🔓'}</Text>
          </TouchableOpacity>
          <Text style={styles.checkboxLabel}>私密群组</Text>
        </View>
        
        <View style={styles.modalActions}>
          <TouchableOpacity style={styles.cancelButton} onPress={onClose}>
            <Text style={styles.cancelText}>取消</Text>
          </TouchableOpacity>
          <TouchableOpacity style={styles.createButton} onPress={handleSubmit}>
            <Text style={styles.createText}>{ICONS.add} 创建</Text>
          </TouchableOpacity>
        </View>
      </View>
    </View>
  );
};

// 群组管理页面组件
const GroupManagementPage = ({ 
  group, 
  members, 
  visible, 
  onClose,
  onUpdateGroup,
  onAddMember,
  onRemoveMember
}: { 
  group: Group | null; 
  members: GroupMember[]; 
  visible: boolean; 
  onClose: () => void;
  onUpdateGroup: (updatedGroup: Group) => void;
  onAddMember: (name: string) => void;
  onRemoveMember: (id: string) => void;
}) => {
  const [editMode, setEditMode] = useState(false);
  const [editedGroup, setEditedGroup] = useState(group);
  const [newMemberName, setNewMemberName] = useState('');

  if (!visible || !group) return null;

  const handleUpdateGroup = () => {
    if (editedGroup) {
      onUpdateGroup(editedGroup);
      setEditMode(false);
    }
  };

  const handleAddMember = () => {
    if (newMemberName.trim()) {
      onAddMember(newMemberName.trim());
      setNewMemberName('');
    }
  };

  return (
    <View style={styles.managementModalOverlay}>
      <View style={styles.managementModalContent}>
        <View style={styles.managementHeader}>
          <Text style={styles.managementTitle}>{group.name} - 管理</Text>
          <TouchableOpacity onPress={onClose}>
            <Text style={styles.closeText}>×</Text>
          </TouchableOpacity>
        </View>
        
        {editMode ? (
          <View style={styles.editForm}>
            <View style={styles.inputGroup}>
              <Text style={styles.inputLabel}>群组名称</Text>
              <TextInput
                style={styles.input}
                value={editedGroup?.name || ''}
                onChangeText={(text) => setEditedGroup(prev => prev ? {...prev, name: text} : null)}
              />
            </View>
            
            <View style={styles.inputGroup}>
              <Text style={styles.inputLabel}>群组描述</Text>
              <TextInput
                style={[styles.input, styles.textArea]}
                value={editedGroup?.description || ''}
                onChangeText={(text) => setEditedGroup(prev => prev ? {...prev, description: text} : null)}
                multiline
                numberOfLines={3}
              />
            </View>
            
            <View style={styles.managementActions}>
              <TouchableOpacity 
                style={styles.cancelButton} 
                onPress={() => {
                  setEditedGroup(group);
                  setEditMode(false);
                }}
              >
                <Text style={styles.cancelText}>取消</Text>
              </TouchableOpacity>
              <TouchableOpacity 
                style={styles.saveButton} 
                onPress={handleUpdateGroup}
              >
                <Text style={styles.saveText}>保存</Text>
              </TouchableOpacity>
            </View>
          </View>
        ) : (
          <ScrollView style={styles.managementContent}>
            <View style={styles.groupInfoSection}>
              <Text style={styles.sectionTitle}>群组信息</Text>
              <Text style={styles.groupDescription}>{group.description}</Text>
              <Text style={styles.groupDetail}>成员数: {group.members}</Text>
              <Text style={styles.groupDetail}>创建时间: {group.createdAt}</Text>
              
              <TouchableOpacity style={styles.editGroupButton} onPress={() => setEditMode(true)}>
                <Text style={styles.editGroupText}>{ICONS.edit} 编辑群组信息</Text>
              </TouchableOpacity>
            </View>
            
            <View style={styles.membersSection}>
              <Text style={styles.sectionTitle}>群成员 ({members.length})</Text>
              
              <View style={styles.addMemberSection}>
                <TextInput
                  style={styles.addMemberInput}
                  value={newMemberName}
                  onChangeText={setNewMemberName}
                  placeholder="输入用户名添加成员"
                />
                <TouchableOpacity style={styles.addMemberButton} onPress={handleAddMember}>
                  <Text style={styles.addMemberText}>{ICONS.add} 添加</Text>
                </TouchableOpacity>
              </View>
              
              <FlatList
                data={members}
                keyExtractor={item => item.id}
                renderItem={({ item }) => (
                  <MemberItem
                    member={item}
                    onRoleChange={(id, role) => {
                      // 更新成员角色的逻辑
                      Alert.alert('角色变更', `${item.name} 的角色更改为 ${role === 'admin' ? '管理员' : role === 'moderator' ? '版主' : '普通成员'}`);
                    }}
                    onRemove={onRemoveMember}
                  />
                )}
                showsVerticalScrollIndicator={false}
              />
            </View>
          </ScrollView>
        )}
      </View>
    </View>
  );
};

// 主页面组件
const GroupManagementApp: React.FC = () => {
  const [groups, setGroups] = useState<Group[]>([
    {
      id: '1',
      name: '工作交流群',
      description: '公司内部工作沟通交流群',
      members: 24,
      admin: '张经理',
      isPrivate: false,
      createdAt: '2023-05-15'
    },
    {
      id: '2',
      name: '家庭群聊',
      description: '家人日常聊天群',
      members: 8,
      admin: '妈妈',
      isPrivate: true,
      createdAt: '2023-01-10'
    },
    {
      id: '3',
      name: '运动爱好者',
      description: '一起运动健身的小伙伴们',
      members: 42,
      admin: '李教练',
      isPrivate: false,
      createdAt: '2023-03-22'
    }
  ]);

  const [members, setMembers] = useState<GroupMember[]>([
    { id: 'm1', name: '张经理', role: 'admin', joinedAt: '2023-05-15' },
    { id: 'm2', name: '小王', role: 'member', joinedAt: '2023-05-16' },
    { id: 'm3', name: '小李', role: 'moderator', joinedAt: '2023-05-17' },
    { id: 'm4', name: '小赵', role: 'member', joinedAt: '2023-05-20' }
  ]);

  const [showCreateModal, setShowCreateModal] = useState(false);
  const [showManagementModal, setShowManagementModal] = useState(false);
  const [selectedGroup, setSelectedGroup] = useState<Group | null>(null);

  const handleCreateGroup = (groupData: Omit<Group, 'id' | 'members' | 'admin' | 'createdAt'>) => {
    const newGroup: Group = {
      ...groupData,
      id: Date.now().toString(),
      members: 1, // 创建者自己
      admin: '我',
      createdAt: new Date().toLocaleDateString('zh-CN')
    };
    
    setGroups([...groups, newGroup]);
  };

  const handleDeleteGroup = (id: string) => {
    Alert.alert(
      '删除群组',
      '确定要删除这个群组吗?此操作无法撤销。',
      [
        { text: '取消', style: 'cancel' },
        { 
          text: '删除', 
          style: 'destructive',
          onPress: () => setGroups(groups.filter(group => group.id !== id))
        }
      ]
    );
  };

  const handleEditGroup = (group: Group) => {
    // 编辑群组的逻辑
    Alert.alert('编辑群组', `编辑群组: ${group.name}`);
  };

  const handleManageGroup = (group: Group) => {
    setSelectedGroup(group);
    setShowManagementModal(true);
  };

  const handleUpdateGroup = (updatedGroup: Group) => {
    setGroups(groups.map(g => g.id === updatedGroup.id ? updatedGroup : g));
    setSelectedGroup(updatedGroup);
  };

  const handleAddMember = (name: string) => {
    const newMember: GroupMember = {
      id: `m${Date.now()}`,
      name,
      role: 'member',
      joinedAt: new Date().toLocaleDateString('zh-CN')
    };
    
    setMembers([...members, newMember]);
    
    // 同时更新群组成员数
    if (selectedGroup) {
      const updatedGroup = { ...selectedGroup, members: selectedGroup.members + 1 };
      setGroups(groups.map(g => g.id === selectedGroup.id ? updatedGroup : g));
      setSelectedGroup(updatedGroup);
    }
  };

  const handleRemoveMember = (id: string) => {
    Alert.alert(
      '移除成员',
      '确定要将此成员从群组中移除吗?',
      [
        { text: '取消', style: 'cancel' },
        { 
          text: '移除', 
          style: 'destructive',
          onPress: () => {
            setMembers(members.filter(member => member.id !== id));
            
            // 更新群组成员数
            if (selectedGroup) {
              const updatedGroup = { ...selectedGroup, members: selectedGroup.members - 1 };
              setGroups(groups.map(g => g.id === selectedGroup.id ? updatedGroup : g));
              setSelectedGroup(updatedGroup);
            }
          }
        }
      ]
    );
  };

  return (
    <SafeAreaView style={styles.container}>
      {/* 头部 */}
      <View style={styles.header}>
        <Text style={styles.title}>群组管理</Text>
        <TouchableOpacity style={styles.addButton} onPress={() => setShowCreateModal(true)}>
          <Text style={styles.addButtonText}>{ICONS.add} 创建群组</Text>
        </TouchableOpacity>
      </View>

      {/* 群组列表 */}
      <FlatList
        data={groups}
        keyExtractor={item => item.id}
        renderItem={({ item }) => (
          <GroupCard
            group={item}
            onEdit={handleEditGroup}
            onDelete={handleDeleteGroup}
            onManage={handleManageGroup}
          />
        )}
        style={styles.groupsList}
        showsVerticalScrollIndicator={false}
        ListHeaderComponent={
          <Text style={styles.sectionTitle}>我的群组 ({groups.length})</Text>
        }
      />

      {/* 底部导航 */}
      <View style={styles.bottomNav}>
        <TouchableOpacity style={styles.navItem}>
          <Text style={styles.navIcon}>{ICONS.group}</Text>
          <Text style={styles.navText}>群组</Text>
        </TouchableOpacity>
        <TouchableOpacity style={styles.navItem}>
          <Text style={styles.navIcon}>{ICONS.member}</Text>
          <Text style={styles.navText}>成员</Text>
        </TouchableOpacity>
        <TouchableOpacity style={[styles.navItem, styles.activeNavItem]}>
          <Text style={styles.navIcon}>{ICONS.settings}</Text>
          <Text style={styles.navText}>管理</Text>
        </TouchableOpacity>
        <TouchableOpacity style={styles.navItem}>
          <Text style={styles.navIcon}>{ICONS.search}</Text>
          <Text style={styles.navText}>发现</Text>
        </TouchableOpacity>
      </View>

      {/* 创建群组模态框 */}
      <CreateGroupModal
        visible={showCreateModal}
        onClose={() => setShowCreateModal(false)}
        onCreate={handleCreateGroup}
      />

      {/* 群组管理页面 */}
      <GroupManagementPage
        group={selectedGroup}
        members={members}
        visible={showManagementModal}
        onClose={() => setShowManagementModal(false)}
        onUpdateGroup={handleUpdateGroup}
        onAddMember={handleAddMember}
        onRemoveMember={handleRemoveMember}
      />
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f8fafc',
  },
  header: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between',
    padding: 20,
    backgroundColor: '#ffffff',
    borderBottomWidth: 1,
    borderBottomColor: '#e2e8f0',
  },
  title: {
    fontSize: 20,
    fontWeight: 'bold',
    color: '#1e293b',
  },
  addButton: {
    backgroundColor: '#3b82f6',
    paddingHorizontal: 16,
    paddingVertical: 8,
    borderRadius: 20,
  },
  addButtonText: {
    color: '#ffffff',
    fontSize: 14,
    fontWeight: '500',
  },
  groupsList: {
    flex: 1,
    padding: 16,
  },
  sectionTitle: {
    fontSize: 18,
    fontWeight: 'bold',
    color: '#1e293b',
    marginVertical: 12,
  },
  groupCard: {
    backgroundColor: '#ffffff',
    borderRadius: 12,
    padding: 16,
    marginBottom: 16,
    elevation: 1,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 1 },
    shadowOpacity: 0.1,
    shadowRadius: 2,
  },
  groupHeader: {
    flexDirection: 'row',
    alignItems: 'center',
    marginBottom: 12,
  },
  groupIcon: {
    width: 48,
    height: 48,
    borderRadius: 24,
    backgroundColor: '#dbeafe',
    alignItems: 'center',
    justifyContent: 'center',
    marginRight: 12,
  },
  groupIconText: {
    fontSize: 24,
  },
  groupInfo: {
    flex: 1,
  },
  groupName: {
    fontSize: 16,
    fontWeight: 'bold',
    color: '#1e293b',
  },
  groupDescription: {
    fontSize: 14,
    color: '#64748b',
    marginTop: 4,
  },
  groupDetails: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    marginBottom: 12,
    paddingBottom: 12,
    borderBottomWidth: 1,
    borderBottomColor: '#e2e8f0',
  },
  detailItem: {
    flexDirection: 'row',
    alignItems: 'center',
  },
  detailIcon: {
    fontSize: 14,
    marginRight: 4,
    color: '#64748b',
  },
  detailText: {
    fontSize: 12,
    color: '#64748b',
  },
  groupActions: {
    flexDirection: 'row',
    justifyContent: 'space-between',
  },
  actionButton: {
    flexDirection: 'row',
    alignItems: 'center',
  },
  actionIcon: {
    fontSize: 16,
    marginRight: 4,
    color: '#64748b',
  },
  actionText: {
    fontSize: 12,
    color: '#64748b',
  },
  manageButton: {
    backgroundColor: '#3b82f6',
    paddingHorizontal: 12,
    paddingVertical: 6,
    borderRadius: 8,
  },
  manageText: {
    color: '#ffffff',
    fontSize: 12,
    fontWeight: '500',
  },
  memberItem: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    paddingVertical: 12,
    borderBottomWidth: 1,
    borderBottomColor: '#e2e8f0',
  },
  memberInfo: {
    flexDirection: 'row',
    alignItems: 'center',
  },
  memberAvatar: {
    fontSize: 20,
    marginRight: 12,
  },
  memberName: {
    fontSize: 14,
    fontWeight: 'bold',
    color: '#1e293b',
  },
  memberRole: {
    fontSize: 12,
    color: '#64748b',
  },
  memberActions: {
    flexDirection: 'row',
  },
  roleButton: {
    backgroundColor: '#f1f5f9',
    paddingHorizontal: 8,
    paddingVertical: 4,
    borderRadius: 4,
    marginRight: 8,
  },
  roleText: {
    fontSize: 12,
    color: '#64748b',
  },
  removeButton: {
    backgroundColor: '#fee2e2',
    paddingHorizontal: 8,
    paddingVertical: 4,
    borderRadius: 4,
  },
  removeText: {
    fontSize: 12,
    color: '#dc2626',
  },
  modalOverlay: {
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
    backgroundColor: 'rgba(0,0,0,0.5)',
    justifyContent: 'center',
    alignItems: 'center',
    zIndex: 100,
  },
  modalContent: {
    backgroundColor: '#ffffff',
    borderRadius: 12,
    padding: 20,
    width: width * 0.8,
  },
  modalTitle: {
    fontSize: 18,
    fontWeight: 'bold',
    color: '#1e293b',
    marginBottom: 16,
    textAlign: 'center',
  },
  inputGroup: {
    marginBottom: 16,
  },
  inputLabel: {
    fontSize: 14,
    color: '#1e293b',
    marginBottom: 8,
  },
  input: {
    borderWidth: 1,
    borderColor: '#cbd5e1',
    borderRadius: 8,
    padding: 12,
    fontSize: 16,
    color: '#1e293b',
  },
  textArea: {
    height: 80,
    textAlignVertical: 'top',
  },
  checkboxGroup: {
    flexDirection: 'row',
    alignItems: 'center',
    marginBottom: 16,
  },
  checkbox: {
    width: 24,
    height: 24,
    borderRadius: 12,
    borderWidth: 1,
    borderColor: '#cbd5e1',
    alignItems: 'center',
    justifyContent: 'center',
    marginRight: 8,
  },
  checkboxText: {
    fontSize: 16,
  },
  checkboxLabel: {
    fontSize: 14,
    color: '#1e293b',
  },
  modalActions: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    marginTop: 16,
  },
  cancelButton: {
    flex: 1,
    padding: 12,
    borderWidth: 1,
    borderColor: '#cbd5e1',
    borderRadius: 8,
    alignItems: 'center',
    marginRight: 8,
  },
  cancelText: {
    color: '#64748b',
    fontWeight: '500',
  },
  createButton: {
    flex: 1,
    padding: 12,
    backgroundColor: '#3b82f6',
    borderRadius: 8,
    alignItems: 'center',
    marginLeft: 8,
  },
  createText: {
    color: '#ffffff',
    fontWeight: '500',
  },
  managementModalOverlay: {
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
    backgroundColor: 'rgba(0,0,0,0.5)',
    justifyContent: 'center',
    alignItems: 'center',
    zIndex: 101,
  },
  managementModalContent: {
    backgroundColor: '#ffffff',
    borderRadius: 12,
    width: width * 0.9,
    height: '80%',
  },
  managementHeader: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    padding: 16,
    borderBottomWidth: 1,
    borderBottomColor: '#e2e8f0',
  },
  managementTitle: {
    fontSize: 18,
    fontWeight: 'bold',
    color: '#1e293b',
  },
  closeText: {
    fontSize: 24,
    color: '#94a3b8',
  },
  managementContent: {
    flex: 1,
    padding: 16,
  },
  groupInfoSection: {
    marginBottom: 20,
  },
  groupDetail: {
    fontSize: 14,
    color: '#64748b',
    marginVertical: 4,
  },
  editGroupButton: {
    backgroundColor: '#f1f5f9',
    padding: 12,
    borderRadius: 8,
    alignItems: 'center',
    marginTop: 12,
  },
  editGroupText: {
    color: '#3b82f6',
    fontWeight: '500',
  },
  membersSection: {
    flex: 1,
  },
  addMemberSection: {
    flexDirection: 'row',
    marginBottom: 16,
  },
  addMemberInput: {
    flex: 1,
    borderWidth: 1,
    borderColor: '#cbd5e1',
    borderRadius: 8,
    padding: 10,
    fontSize: 14,
    color: '#1e293b',
    marginRight: 8,
  },
  addMemberButton: {
    backgroundColor: '#3b82f6',
    paddingHorizontal: 12,
    paddingVertical: 10,
    borderRadius: 8,
  },
  addMemberText: {
    color: '#ffffff',
    fontWeight: '500',
  },
  editForm: {
    padding: 16,
  },
  managementActions: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    marginTop: 16,
  },
  saveButton: {
    flex: 1,
    padding: 12,
    backgroundColor: '#3b82f6',
    borderRadius: 8,
    alignItems: 'center',
    marginLeft: 8,
  },
  saveText: {
    color: '#ffffff',
    fontWeight: '500',
  },
  saveButton: {
    flex: 1,
    padding: 12,
    backgroundColor: '#3b82f6',
    borderRadius: 8,
    alignItems: 'center',
    marginLeft: 8,
  },
  bottomNav: {
    flexDirection: 'row',
    justifyContent: 'space-around',
    backgroundColor: '#ffffff',
    borderTopWidth: 1,
    borderTopColor: '#e2e8f0',
    paddingVertical: 12,
  },
  navItem: {
    alignItems: 'center',
    flex: 1,
  },
  activeNavItem: {
    paddingBottom: 2,
    borderBottomWidth: 2,
    borderBottomColor: '#3b82f6',
  },
  navIcon: {
    fontSize: 20,
    color: '#94a3b8',
    marginBottom: 4,
  },
  activeNavIcon: {
    color: '#3b82f6',
  },
  navText: {
    fontSize: 12,
    color: '#94a3b8',
  },
  activeNavText: {
    color: '#3b82f6',
    fontWeight: '500',
  },
});

export default GroupManagementApp;

请添加图片描述

打包

接下来通过打包命令npn run harmony将reactNative的代码打包成为bundle,这样可以进行在开源鸿蒙OpenHarmony中进行使用。

在这里插入图片描述

打包之后再将打包后的鸿蒙OpenHarmony文件拷贝到鸿蒙的DevEco-Studio工程目录去:

在这里插入图片描述

最后运行效果图如下显示:

请添加图片描述

欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐