这个剧本杀组队聊天对话页面采用了清晰的组件化架构,通过单一组件 TeamChatDialog 实现了完整的聊天功能。组件设计注重用户体验和交互性,通过合理的布局和样式,营造出专业、社交化的应用氛围。

状态管理

const [detailVisible, setDetailVisible] = useState(false);
const [detailTitle, setDetailTitle] = useState<string | null>(null);

使用 useState Hook 管理两个核心状态:

  1. detailVisible:控制详情面板的显示/隐藏,初始值为 false
  2. detailTitle:存储当前查看的消息标题,初始值为 null

这种状态管理方式简洁明了,适合处理详情面板的显示和隐藏逻辑,以及存储当前查看的消息信息。

消息回复功能

const onReply = (who: string) => Alert.alert('聊天', `回复:${who}`);

消息回复功能通过 Alert.alert 显示回复信息,提供了基本的用户反馈。当用户点击"回复"按钮时,会调用 onReply 函数,显示回复对象的信息。

消息详情

const onMore = (title: string) => {
  setDetailTitle(title);
  setDetailVisible(true);
};

const onCloseDetail = () => {
  setDetailVisible(false);
  setDetailTitle(null);
};

消息详情查看功能通过更新状态实现详情面板的显示和隐藏。当用户点击"详情"按钮时,会调用 onMore 函数,设置消息标题并显示详情面板;当用户点击"关闭"按钮时,会调用 onCloseDetail 函数,隐藏详情面板并清空消息标题。

提醒设置

const onNotify = () => Alert.alert('提醒设置', '已开启新消息提醒');

提醒设置功能通过 Alert.alert 显示提醒设置信息,提供了基本的用户反馈。当用户点击"开启提醒"按钮时,会调用 onNotify 函数,显示提醒设置成功的信息。

消息气泡

<View style={styles.bubbleRow}>
  <Image source={{ uri: ICONS_BASE64.sticker }} style={styles.avatar} />
  <View style={styles.bubbleLeft}>
    <Text style={styles.bubbleName}>队长</Text>
    <Text style={styles.bubbleText}>集合时间定在周六下午两点,大家都可以吗?</Text>
    <Text style={styles.bubbleTime}>13:05</Text>
  </View>
  <TouchableOpacity onPress={() => onReply('队长')}>
    <Text style={styles.bubbleAction}>回复</Text>
  </TouchableOpacity>
  <TouchableOpacity onPress={() => onMore('队长 · 消息详情')}>
    <Text style={styles.bubbleAction}>详情</Text>
  </TouchableOpacity>
</View>

消息气泡设计采用了左侧头像、左侧消息内容、右侧操作按钮的布局结构,这种布局在聊天应用中非常常见,能够清晰地展示消息发送者、内容和时间,并提供基本的交互功能。

详情面板

{detailVisible && (
  <View style={styles.detailOverlay}>
    <View style={styles.detailPanel}>
      {/* 详情面板内容 */}
    </View>
  </View>
)}

详情面板通过条件渲染实现,当 detailVisibletrue 时显示。面板采用覆盖层的形式,包含消息标题、详细信息和操作按钮,提供了清晰的消息详情展示和交互功能。

页面布局

<SafeAreaView style={styles.container}>
  <View style={styles.header}>
    {/* 头部内容 */}
  </View>

  <ScrollView style={styles.content}>
    {/* 消息气泡 */}
    <View style={styles.bubbleRow}>
      {/* 消息内容 */}
    </View>
    {/* 更多消息气泡 */}
    <View style={styles.sectionAlt}>
      {/* 设置内容 */}
    </View>
  </ScrollView>

  <View style={styles.footer}>
    {/* 底部操作按钮 */}
  </View>

  {/* 详情面板 */}
</SafeAreaView>

页面布局采用了分层设计:

  • 头部:包含页面标题和操作图标
  • 内容区:使用 ScrollView 实现可滚动的消息列表和设置区域
  • 底部:包含快捷操作按钮
  • 详情面板:采用覆盖层的形式,显示消息的详细信息

这种布局设计清晰明了,用户可以直观地理解和使用页面的功能。

消息气泡样式

消息气泡样式注重视觉层次感和用户体验:

  • 左侧消息:使用浅色背景,与页面背景形成对比
  • 右侧消息(自己发送的):使用蓝色背景,与左侧消息形成对比
  • 消息发送者名称:使用较小字体和浅色,与消息内容形成对比
  • 消息时间:使用更小字体和浅色,作为辅助信息
  • 操作按钮:使用标签样式,提供清晰的交互反馈

  1. 组件映射
    • SafeAreaViewSafeAreaFlex 容器
    • ViewFlex 容器
    • TextText 组件
    • ImageImage 组件
    • TouchableOpacityButtonText + 手势事件
    • ScrollViewScroll 组件
    • AlertDialog 组件

  1. 组件复用

    • 考虑将消息气泡、详情面板等拆分为独立的组件,提高代码的可维护性:
    const MessageBubble = ({ 
      sender, 
      text, 
      time, 
      isOwn = false, 
      onReply, 
      onMore 
    }) => (
      <View style={[styles.bubbleRow, isOwn && styles.bubbleRowRight]}>
        {!isOwn && <Image source={{ uri: ICONS_BASE64.sticker }} style={styles.avatar} />}
        <View style={[isOwn ? styles.bubbleRight : styles.bubbleLeft]}>
          <Text style={isOwn ? styles.bubbleNameRight : styles.bubbleName}>{sender}</Text>
          <Text style={isOwn ? styles.bubbleTextRight : styles.bubbleText}>{text}</Text>
          <Text style={isOwn ? styles.bubbleTimeRight : styles.bubbleTime}>{time}</Text>
        </View>
        {isOwn ? (
          <Image source={{ uri: ICONS_BASE64.sticker }} style={styles.avatar} />
        ) : (
          <>
            <TouchableOpacity onPress={() => onReply(sender)}>
              <Text style={styles.bubbleAction}>回复</Text>
            </TouchableOpacity>
            <TouchableOpacity onPress={() => onMore(`${sender} · 消息详情`)}>
              <Text style={styles.bubbleAction}>详情</Text>
            </TouchableOpacity>
          </>
        )}
      </View>
    );
    
  2. 数据管理

    • 使用数据驱动的方式管理消息列表,减少重复代码:
    const messages = [
      { id: 1, sender: '队长', text: '集合时间定在周六下午两点,大家都可以吗?', time: '13:05', isOwn: false },
      { id: 2, sender: '糖糖', text: '我可以,店里有休息区吗?', time: '13:07', isOwn: false },
      { id: 3, sender: '我', text: '有的,我也到店里先踩点。', time: '13:09', isOwn: true },
      // 更多消息...
    ];
    
  3. 图片优化

    • 对于 Base64 编码的图片,考虑使用更高效的图片格式和加载方式:
    // 可以考虑使用 require 加载本地图片
    <Image source={require('./assets/avatar.png')} style={styles.avatar} />
    
  4. 可访问性

    • 添加 accessibilityLabel 属性,提高应用的可访问性:
    <TouchableOpacity 
      onPress={() => onReply('队长')}
      accessibilityLabel="回复队长的消息"
    >
      <Text style={styles.bubbleAction}>回复</Text>
    </TouchableOpacity>
    
  5. 动画效果

    • 添加简单的动画效果,提升用户体验:
    import { Animated } from 'react-native';
    
    const fadeAnim = useRef(new Animated.Value(0)).current;
    
    useEffect(() => {
      if (detailVisible) {
        Animated.timing(fadeAnim, {
          toValue: 1,
          duration: 300,
          useNativeDriver: true,
        }).start();
      }
    }, [detailVisible]);
    
    // 在样式中使用
    style={[styles.detailOverlay, { opacity: fadeAnim }]}
    

这个剧本杀组队聊天对话页面展示了 React Native 跨端开发的核心技术要点,通过合理的组件设计、清晰的状态管理、直观的用户界面和完整的功能实现,为用户提供了一个功能齐全、体验良好的聊天工具。在鸿蒙系统适配方面,通过组件映射、样式适配和状态管理调整,可以快速实现跨端迁移,保持功能和视觉效果的一致性。


剧本杀组队聊天对话页是典型的移动端即时通讯类页面,核心承载单/多人会话展示、消息交互、快捷操作、模态详情等IM场景核心能力,也是React Native鸿蒙跨端开发中左右侧气泡布局、固定底部操作栏、轻量状态管理、跨端样式一致性的关键验证场景。本次实现的TeamChatDialog组件基于React Native原生基础组件开发,无第三方IM库与原生依赖,以Base64格式统一管理聊天类小图标资源,通过useState轻量实现模态详情层的显隐与数据传递,所有交互逻辑均为纯JS编写,完全契合React Native向鸿蒙跨端的核心设计要求——Base64资源跨端直接复用,Flex气泡布局可映射为ArkUI通用布局,原生基础组件通过桥接层无缝转换,纯JS逻辑无需修改即可在鸿蒙端运行

作为IM类核心页面,其开发与适配思路可复用于社交、组队、办公等场景的聊天页面开发,以下将从架构设计、核心IM能力实现、跨端友好开发细节、鸿蒙端实操适配要点等维度,深度解读该代码的技术设计与跨端适配逻辑。

整体架构设计:IM类页面的轻量高内聚实现与跨端原则

即时通讯类页面的核心设计诉求是布局简洁、交互流畅、状态轻量、视觉贴合IM用户习惯,同时需保证跨端适配时修改范围高度收敛。本次TeamChatDialog组件遵循高内聚、无冗余、贴合原生IM交互的设计原则,将页面拆分为头部导航、聊天消息区、设置提示区、底部固定操作栏、模态消息详情层五大核心模块,所有模块的视图渲染、样式设计、交互逻辑均收敛在单个函数式组件中,未做过度组件拆分。这种设计既保证了IM页面的核心交互连贯性,又契合React Native鸿蒙跨端规范——单组件的高内聚实现让鸿蒙端适配无需跨多个组件进行桥接与逻辑调整,所有样式与API修改均集中在组件内部,大幅降低端侧改造成本,同时极简的组件结构让桥接层的解析与映射效率更高,保证聊天页面在鸿蒙端的渲染与交互性能。

页面的依赖导入严格遵循跨端通用原则,仅引入React核心库、useState轻量Hook与RN原生基础组件,其中SafeAreaViewViewTextTouchableOpacityScrollViewImage为RN与鸿蒙ArkUI通用的基础渲染组件,Alert为跨端通用的原生提示API。这些组件与API均已在华为开源的react-native-harmony桥接层中实现与ArkUI的一一映射,无需开发自定义桥接模块即可完成基础适配,从依赖层面规避了跨端的原生适配成本,避免因引入第三方库导致的鸿蒙端桥接开发工作量。

全局层面的ICONS_BASE64常量对象设计,是本次IM页面实现的核心跨端友好细节,将聊天、发送、贴纸、消息提醒四类IM场景高频小图标统一转换为Base64格式管理,替代了传统的本地图片资源与第三方图标库。对于IM类页面的高频小图标,Base64格式是跨端资源管理的最优解——传统本地图片需要在iOS、Android、鸿蒙各端分别配置资源目录与路径,适配繁琐且易出现资源路径错误;第三方图标库可能存在原生依赖,鸿蒙端需要开发对应的桥接模块;而Base64格式以纯字符串形式存储图标资源,无需依赖任何端侧资源目录,可在RN与ArkUI的Image组件中直接加载,实现一次编码,多端复用,同时集中式的常量对象管理让IM图标资源的维护与替换更便捷,鸿蒙端若需替换图标,仅需修改对应键值的Base64字符串即可,无需修改任何渲染逻辑。

页面的状态管理采用React内置的useState轻量实现,仅定义detailVisible(模态详情层显隐状态)与detailTitle(消息详情标题)两个状态,精准覆盖模态层的核心交互需求,无复杂的状态流转与数据共享。所有交互回调函数(onReplyonNotifyonMoreonCloseDetail)均为组件内部定义的纯函数,逻辑简洁且与平台无关,跨端时可直接复用。这种轻量的状态与逻辑设计,让IM页面的核心能力与原生平台完全解耦,成为跨端复用的核心资产。

核心IM能力实现:

本次实现的剧本杀组队聊天页,核心覆盖左右侧气泡消息布局、聊天消息时间戳/昵称展示、固定底部操作栏、消息回复/详情交互、模态消息详情层五大IM类页面必备能力,所有能力的实现均基于RN原生基础组件与跨端通用的开发方式,从布局、资源、交互、样式四个维度践行了跨端友好原则,其核心实现逻辑可直接复用于鸿蒙端,仅需做轻量的样式微调即可贴合鸿蒙端的设计规范与IM用户习惯。

左右侧气泡消息:

左右侧气泡布局是IM类页面的核心视觉与布局特征,用于区分他人消息与自己的消息,本次实现通过RN的Flex弹性布局打造了跨端通用的气泡消息布局,完美贴合移动端IM用户的视觉习惯,且布局逻辑可直接映射为ArkUI的Flex布局。核心实现逻辑为基于bubbleRow基础横向布局,通过样式复用+差异化样式覆盖实现左右侧气泡的区分:

  1. 基础布局:所有消息行均基于bubbleRow样式,设置flexDirection: rowalignItems: 'flex-start',保证头像、消息气泡、操作按钮的纵向顶部对齐,契合IM消息的展示习惯;
  2. 他人消息(左侧气泡):直接使用bubbleRow基础样式,按「头像-左侧气泡-回复按钮-详情按钮」的顺序排列,头像设置marginRight: 8与气泡形成间距,左侧气泡设置maxWidth: '70%'避免消息过宽,同时添加轻量阴影提升视觉层级;
  3. 自己消息(右侧气泡):通过bubbleRowRight样式覆盖基础布局,添加justifyContent: 'flex-end'实现整体右对齐,调整子元素顺序为「右侧气泡-头像」,并为右侧气泡设置差异化的背景色,与左侧气泡形成视觉区分,同时保留maxWidth: '70%'的通用限制;
  4. 自适应适配:气泡的宽度通过maxWidth: '70%'做自适应限制,高度随消息文本自动撑开,无需硬编码尺寸,可适配不同长度的消息内容,且在不同宽度的iOS、Android、鸿蒙设备上均能保持一致的展示效果。

该气泡布局的所有Flex属性(flexDirectionalignItemsjustifyContent)、间距属性(marginpadding)均为RN与ArkUI的通用属性,跨端时无需修改任何布局逻辑,鸿蒙端可通过桥接层直接映射为ArkUI的对应布局属性,实现与RN端高度一致的IM气泡展示效果。

聊天消息:

完整的IM消息展示需包含头像、发送者昵称、消息文本、时间戳四大核心要素,本次实现通过Flex布局与差异化样式,打造了跨端通用的消息精细化展示方案,所有样式属性均为RN与ArkUI的通用属性,跨端可直接复用。

  1. 头像:采用Base64图标作为占位,设置固定宽高与圆形圆角(borderRadius: 16,为宽高的50%),背景色与页面主色调相呼应,头像的尺寸与圆角规范贴合移动端IM的通用设计,鸿蒙端仅需微调圆角值即可贴合端侧设计规范;
  2. 发送者昵称:区分他人与自己的消息,分别设置不同的文字颜色,字号采用小尺寸(12px),与消息文本形成视觉层级,marginTop: 2保证昵称与文本的合理间距;
  3. 消息文本:采用中等字号(13px),颜色为深灰色保证可读性,随气泡宽度自适应换行,无需额外的换行处理,契合RN与ArkUI的文本渲染特性;
  4. 时间戳:采用最小字号(11px),浅灰色弱化展示,marginTop: 4与消息文本分隔,他人消息左对齐、自己消息右对齐,贴合IM用户的时间戳查看习惯。

所有消息要素的样式均遵循差异化与统一性结合的原则,统一的间距、字号规范保证了消息展示的整洁性,差异化的颜色、对齐方式实现了他人与自己消息的清晰区分,且所有样式属性均无RN平台特有语法,跨端可直接复用。

固定底部操作栏:

IM类页面的核心交互特征是底部操作栏固定在屏幕底部,不随消息区滚动,本次实现通过RN的布局特性打造了跨端通用的固定底部操作栏,核心逻辑可直接映射为ArkUI的固定布局实现。底部操作栏的根容器footer未嵌套在ScrollView消息区内,而是作为SafeAreaView的直接子元素,通过flexDirection: row实现两个快捷操作按钮的横向均分,flex: 1让按钮适配不同设备的屏幕宽度,同时设置backgroundColorborderTopWidth实现与消息区的视觉分隔,贴合IM页面的原生设计习惯。

操作栏内的按钮采用图文混排设计,以TouchableOpacity为点击容器,内部通过flexDirection: row + alignItems: 'center'实现Base64小图标与文本的横向居中排列,marginRight: 6保证图标与文本的合理间距。TouchableOpacity提供的点击透明度反馈效果,在iOS、Android端表现一致,在鸿蒙端可通过桥接层映射为ArkUI的Button组件+Gesture手势,保留核心的点击反馈逻辑,保证跨端交互体验的一致性。同时通过差异化的背景色与文字色区分普通发送按钮与提醒设置按钮,突出核心操作,符合移动端IM的通用交互规范。

消息交互与模态详情层:

IM类页面的核心交互包括消息回复、消息详情查看、快捷操作,本次实现通过纯JS回调函数与轻量状态管理,打造了跨端通用的消息交互方案,其中消息详情采用模态层展示,是移动端IM查看消息详情的通用设计,其实现逻辑为RN与ArkUI的通用方案,跨端可直接复用。

  1. 基础消息交互:onReplyonNotify等回调函数为纯JS实现,内部仅调用RN的通用原生APIAlert.alert实现交互反馈,无任何端侧特有代码。该API在鸿蒙端可通过桥接层映射为ArkUI的AlertDialog.show() API,核心的交互触发逻辑与提示内容无需修改,仅需轻量调整API的调用方式即可;
  2. 模态详情层实现:通过useState定义的detailVisibledetailTitle两个状态,分别控制模态层的显隐与详情页标题的传递。点击消息的「详情」按钮触发onMore函数,更新状态并实现模态层的展示;点击关闭按钮触发onCloseDetail函数,重置状态实现模态层的隐藏,状态流转简洁且无冗余。模态层的渲染通过React的条件渲染实现({detailVisible && ...}),无RN特有语法,跨端可完全复用;
  3. 模态层布局:模态层的根容器detailOverlay通过position: 'absolute'脱离文档流,设置left: 0, right: 0, top: 0, bottom: 0实现全屏遮罩,backgroundColor: 'rgba(0,0,0,0.25)'实现半透明遮罩效果,justifyContent: 'center' + alignItems: 'center'保证详情面板垂直水平居中,padding: 16避免面板在小屏设备上贴边。详情面板设置width: '100%' + maxWidth: 420实现不同设备的自适应宽度,内部分为头部(标题+关闭)、主体(消息详情)、底部(快捷操作)三个模块,所有内部布局均为跨端通用的Flex实现,贴合移动端模态层的通用设计习惯。

即时通讯类页面的跨端开发核心诉求是气泡布局的跨端兼容、IM视觉样式的统一适配、资源与逻辑的跨端复用、贴合各端IM用户习惯,本次实现从资源管理、布局设计、样式规范、逻辑实现、组件使用五个维度,打造了跨端友好的开发细节,这些细节不仅保证了页面在RN端的视觉贴合IM习惯、交互流畅,更让鸿蒙端的适配工作实现零逻辑修改、轻量样式微调、资源直接复用,是IM类页面React Native鸿蒙跨端开发的最佳实践。

IM页面:

IM类页面包含聊天、发送、提醒、贴纸等高频小图标,本地小图标资源的跨端兼容是核心痛点之一,本次实现将所有IM类小图标转换为Base64格式,封装在ICONS_BASE64常量对象中,从根本上解决了这一痛点。与传统的本地图片资源、远程图片资源相比,Base64格式在IM类页面的跨端开发中具备三大核心优势:

  1. 无需端侧资源配置:Base64以纯字符串形式存在,无需在iOS、Android、鸿蒙各端分别配置资源目录、修改资源路径,实现一次编码多端复用。对于IM类这种小图标高频使用的页面,可大幅减少跨端的资源适配工作量,避免因资源路径错误导致的图标展示异常;
  2. 加载效率更高:Base64资源内嵌在代码中,无需发起网络请求或读取本地文件,避免了图标加载耗时导致的页面视觉空白,尤其是IM页面的底部操作栏与消息区图标,即时加载能保证交互的流畅性;
  3. 桥接层兼容更好:RN与ArkUI的Image组件均原生支持Base64格式的uri加载,无需开发任何资源解析的桥接模块,适配成本为零,且图标展示效果在各端保持高度一致。

同时,集中式的常量对象管理让IM类小图标的维护更便捷,鸿蒙端若需替换某类图标(如更换发送图标样式),仅需修改对应键值的Base64字符串即可,无需修改任何渲染与加载逻辑,保证了代码的可维护性。


本次基于React Native实现的剧本杀组队聊天对话页,作为典型的即时通讯类页面,全程践行了跨端友好型开发原则,通过Base64格式集中管理IM类高频小图标、打造Flex弹性布局适配IM所有场景、采用贴合IM习惯的模块化样式设计并遵循RN驼峰规范、纯JS实现所有交互逻辑并使用轻量useState管理状态、仅使用RN原生基础组件与通用API、高内聚单组件实现IM核心功能,成为React Native鸿蒙跨端开发的IM类页面最佳实践案例。从该页面的实现与适配解析中,可提炼出即时通讯类页面的React Native鸿蒙跨端开发核心原则与实践启示,这些原则能有效降低IM类页面的鸿蒙适配成本,实现零逻辑修改、轻量样式微调、快速适配

  1. Base64格式集中管理IM类高频本地小图标:对于IM页面的聊天、发送、提醒等高频小图标,优先转换为Base64格式封装在常量对象中,实现一次编码多端复用,规避端侧资源目录配置成本,同时集中式管理让图标维护与替换更便捷;
  2. Flex弹性布局适配IM所有布局场景:采用Flex布局实现头部导航、左右侧气泡消息、固定底部操作栏、模态详情层等所有IM布局,通过样式覆盖实现左右侧气泡的区分,拒绝硬编码像素值,保证页面在不同尺寸的iOS、Android、鸿蒙设备上的展示一致性;
  3. 贴合IM用户习惯的模块化样式设计:按IM核心视觉元素(气泡、消息要素、操作按钮)定义独立的样式类,实现样式复用与视觉区分,所有样式遵循RN驼峰命名法与通用属性原则,与ArkUI样式规范高度兼容;
  4. 纯JS/TS实现交互逻辑+轻量状态管理:所有IM交互回调函数均为纯JS/TS实现,与端侧原生代码解耦;状态管理采用useState轻量实现,仅覆盖模态层显隐、简单数据传递等核心需求,让核心逻辑成为跨端复用的核心资产;
  5. 极简依赖:仅使用RN原生基础组件与通用API:避免引入第三方IM库、组件库与原生模块,依托RN原生基础组件实现IM的所有布局与交互效果,利用react-native-harmony桥接层的成熟映射方案,实现组件的无缝桥接;
  6. 高内聚单组件实现IM核心功能:IM页面无需过度组件拆分,将所有核心模块的视图、逻辑、样式收敛在单个组件中,让鸿蒙端适配的修改范围高度收敛,所有样式与API修改均集中在组件内部;
  7. 贴合原生IM交互设计:采用左右侧气泡、固定底部操作栏、模态消息详情层等原生IM通用交互设计,避免使用RN特有语法与端侧特有交互,保证跨端的IM交互体验一致性,同时降低用户的跨端学习成本。

真实演示案例代码:




import React, { useState } from 'react';
import { SafeAreaView, View, Text, StyleSheet, TouchableOpacity, ScrollView, Alert, Image } from 'react-native';

const ICONS_BASE64 = {
  chat: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNgYAAAAAMAASsJTYQAAAAASUVORK5CYII=',
  send: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNgYAAAAAMAASsJTYQAAAAASUVORK5CYII=',
  sticker: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNgYAAAAAMAASsJTYQAAAAASUVORK5CYII=',
  bell: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNgYAAAAAMAASsJTYQAAAAASUVORK5CYII=',
};

const TeamChatDialog: React.FC = () => {
  const [detailVisible, setDetailVisible] = useState(false);
  const [detailTitle, setDetailTitle] = useState<string | null>(null);
  const onReply = (who: string) => Alert.alert('聊天', `回复:${who}`);
  const onNotify = () => Alert.alert('提醒设置', '已开启新消息提醒');
  const onMore = (title: string) => {
    setDetailTitle(title);
    setDetailVisible(true);
  };
  const onCloseDetail = () => {
    setDetailVisible(false);
    setDetailTitle(null);
  };

  return (
    <SafeAreaView style={styles.container}>
      <View style={styles.header}>
        <Text style={styles.title}>剧本杀组队 · 聊天对话</Text>
        <View style={styles.headerIcons}>
          <Image source={{ uri: ICONS_BASE64.chat }} style={styles.headerIconImg} />
          <Text style={styles.headerEmoji}>💬</Text>
        </View>
      </View>

      <ScrollView style={styles.content}>
        <View style={styles.bubbleRow}>
          <Image source={{ uri: ICONS_BASE64.sticker }} style={styles.avatar} />
          <View style={styles.bubbleLeft}>
            <Text style={styles.bubbleName}>队长</Text>
            <Text style={styles.bubbleText}>集合时间定在周六下午两点,大家都可以吗?</Text>
            <Text style={styles.bubbleTime}>13:05</Text>
          </View>
          <TouchableOpacity onPress={() => onReply('队长')}>
            <Text style={styles.bubbleAction}>回复</Text>
          </TouchableOpacity>
          <TouchableOpacity onPress={() => onMore('队长 · 消息详情')}>
            <Text style={styles.bubbleAction}>详情</Text>
          </TouchableOpacity>
        </View>
        <View style={styles.bubbleRow}>
          <Image source={{ uri: ICONS_BASE64.sticker }} style={styles.avatar} />
          <View style={styles.bubbleLeft}>
            <Text style={styles.bubbleName}>糖糖</Text>
            <Text style={styles.bubbleText}>我可以,店里有休息区吗?</Text>
            <Text style={styles.bubbleTime}>13:07</Text>
          </View>
          <TouchableOpacity onPress={() => onReply('糖糖')}>
            <Text style={styles.bubbleAction}>回复</Text>
          </TouchableOpacity>
          <TouchableOpacity onPress={() => onMore('糖糖 · 消息详情')}>
            <Text style={styles.bubbleAction}>详情</Text>
          </TouchableOpacity>
        </View>
        <View style={[styles.bubbleRow, styles.bubbleRowRight]}>
          <View style={styles.bubbleRight}>
            <Text style={styles.bubbleNameRight}></Text>
            <Text style={styles.bubbleTextRight}>有的,我也到店里先踩点。</Text>
            <Text style={styles.bubbleTimeRight}>13:09</Text>
          </View>
          <Image source={{ uri: ICONS_BASE64.sticker }} style={styles.avatar} />
        </View>

        <View style={styles.sectionAlt}>
          <Text style={styles.sectionTitle}>设置</Text>
          <View style={styles.tipRow}>
            <Image source={{ uri: ICONS_BASE64.bell }} style={styles.tipIcon} />
            <Text style={styles.tipText}>新消息提醒 · 群内静音 · 置顶会话</Text>
          </View>
        </View>
      </ScrollView>

      <View style={styles.footer}>
        <TouchableOpacity style={styles.sendBtn} onPress={() => Alert.alert('发送', '已发送快捷消息:马上到')}>
          <Image source={{ uri: ICONS_BASE64.send }} style={styles.sendIcon} />
          <Text style={styles.sendText}>发送“马上到”</Text>
        </TouchableOpacity>
        <TouchableOpacity style={[styles.sendBtn, styles.notifyBtn]} onPress={onNotify}>
          <Image source={{ uri: ICONS_BASE64.bell }} style={styles.sendIcon} />
          <Text style={styles.notifyText}>开启提醒</Text>
        </TouchableOpacity>
      </View>
      {detailVisible && (
        <View style={styles.detailOverlay}>
          <View style={styles.detailPanel}>
            <View style={styles.detailHeader}>
              <Text style={styles.detailTitle}>{detailTitle}</Text>
              <TouchableOpacity onPress={onCloseDetail}>
                <Text style={styles.detailClose}>关闭</Text>
              </TouchableOpacity>
            </View>
            <View style={styles.detailBody}>
              <View style={styles.detailRow}>
                <Image source={{ uri: ICONS_BASE64.chat }} style={styles.detailIcon} />
                <Text style={styles.detailText}>内容详情:消息文本与时间戳。</Text>
              </View>
              <View style={styles.detailRow}>
                <Image source={{ uri: ICONS_BASE64.bell }} style={styles.detailIcon} />
                <Text style={styles.detailText}>操作:设置提醒或置顶该会话。</Text>
              </View>
            </View>
            <View style={styles.detailFooter}>
              <TouchableOpacity style={styles.detailBtn} onPress={() => Alert.alert('置顶', '已置顶该会话')}>
                <Text style={styles.detailBtnText}>置顶会话</Text>
              </TouchableOpacity>
              <TouchableOpacity style={[styles.detailBtn, styles.detailBtnPrimary]} onPress={() => Alert.alert('收藏', '已收藏该消息')}>
                <Text style={styles.detailBtnTextPrimary}>收藏消息</Text>
              </TouchableOpacity>
            </View>
          </View>
        </View>
      )}
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: { flex: 1, backgroundColor: '#f0f9ff' },
  header: { padding: 16, backgroundColor: '#ffffff', borderBottomWidth: 1, borderBottomColor: '#bae6fd', flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' },
  title: { fontSize: 18, fontWeight: 'bold', color: '#0f172a' },
  headerIcons: { flexDirection: 'row', alignItems: 'center' },
  headerEmoji: { fontSize: 18, marginLeft: 8 },
  headerIconImg: { width: 24, height: 24 },
  content: { padding: 16 },
  bubbleRow: { flexDirection: 'row', alignItems: 'flex-start', marginBottom: 12 },
  bubbleRowRight: { justifyContent: 'flex-end' },
  avatar: { width: 32, height: 32, borderRadius: 16, marginRight: 8, backgroundColor: '#e0f2fe' },
  bubbleLeft: { backgroundColor: '#ffffff', borderRadius: 12, padding: 10, maxWidth: '70%', shadowColor: '#000', shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.08, shadowRadius: 2 },
  bubbleRight: { backgroundColor: '#dcfce7', borderRadius: 12, padding: 10, maxWidth: '70%', shadowColor: '#000', shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.08, shadowRadius: 2, marginRight: 8 },
  bubbleName: { fontSize: 12, color: '#0284c7', fontWeight: '600' },
  bubbleText: { fontSize: 13, color: '#0f172a', marginTop: 2 },
  bubbleTime: { fontSize: 11, color: '#64748b', marginTop: 4 },
  bubbleAction: { fontSize: 11, color: '#0284c7', backgroundColor: '#bae6fd', paddingHorizontal: 6, paddingVertical: 2, borderRadius: 8, marginLeft: 6, alignSelf: 'flex-start' },
  bubbleNameRight: { fontSize: 12, color: '#16a34a', fontWeight: '600' },
  bubbleTextRight: { fontSize: 13, color: '#0f172a', marginTop: 2 },
  bubbleTimeRight: { fontSize: 11, color: '#64748b', marginTop: 4, textAlign: 'right' },
  sectionAlt: { backgroundColor: '#e0f2fe', borderRadius: 12, padding: 14, marginTop: 8 },
  sectionTitle: { fontSize: 16, fontWeight: 'bold', color: '#0f172a', marginBottom: 10 },
  tipRow: { flexDirection: 'row', alignItems: 'center' },
  tipIcon: { width: 22, height: 22, marginRight: 6 },
  tipText: { fontSize: 12, color: '#475569' },
  footer: { flexDirection: 'row', justifyContent: 'space-between', padding: 12, backgroundColor: '#ffffff', borderTopWidth: 1, borderTopColor: '#bae6fd' },
  sendBtn: { flex: 1, backgroundColor: '#f1f5f9', borderRadius: 12, paddingVertical: 10, flexDirection: 'row', justifyContent: 'center', alignItems: 'center', marginRight: 10 },
  notifyBtn: { backgroundColor: '#bae6fd', marginRight: 0 },
  sendIcon: { width: 16, height: 16, marginRight: 6 },
  sendText: { fontSize: 14, color: '#334155', fontWeight: '500' },
  notifyText: { fontSize: 14, color: '#075985', fontWeight: '600' },
  detailOverlay: { position: 'absolute', left: 0, right: 0, top: 0, bottom: 0, backgroundColor: 'rgba(0,0,0,0.25)', justifyContent: 'center', alignItems: 'center', padding: 16 },
  detailPanel: { width: '100%', maxWidth: 420, backgroundColor: '#ffffff', borderRadius: 14, padding: 14, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.12, shadowRadius: 4 },
  detailHeader: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' },
  detailTitle: { fontSize: 16, fontWeight: '700', color: '#0f172a' },
  detailClose: { fontSize: 12, color: '#075985', backgroundColor: '#bae6fd', paddingHorizontal: 8, paddingVertical: 4, borderRadius: 10 },
  detailBody: { marginTop: 10 },
  detailRow: { flexDirection: 'row', alignItems: 'center', marginTop: 8 },
  detailIcon: { width: 18, height: 18, marginRight: 6 },
  detailText: { fontSize: 12, color: '#475569' },
  detailFooter: { flexDirection: 'row', justifyContent: 'flex-end', marginTop: 12 },
  detailBtn: { backgroundColor: '#f1f5f9', borderRadius: 10, paddingVertical: 8, paddingHorizontal: 12, marginRight: 8 },
  detailBtnPrimary: { backgroundColor: '#bae6fd' },
  detailBtnText: { fontSize: 12, color: '#334155', fontWeight: '600' },
  detailBtnTextPrimary: { fontSize: 12, color: '#075985', fontWeight: '700' },
});

export default TeamChatDialog;

请添加图片描述


打包

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

在这里插入图片描述

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

在这里插入图片描述

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

请添加图片描述
该剧本杀组队聊天系统采用组件化设计,通过TeamChatDialog组件实现完整功能。核心特性包括:1) 使用useState管理详情面板显示状态;2) 提供消息回复、详情查看和提醒设置功能;3) 采用分层布局结构(头部、内容区、底部和详情面板);4) 消息气泡区分左右样式,支持回复和详情操作。系统注重用户体验优化,包括清晰的视觉层次、交互反馈和组件复用建议,同时提出了数据驱动管理和可访问性改进方向。

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

Logo

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

更多推荐