社团公告详情页作为典型的轻交互、强展示类移动端页面,其 React Native 实现聚焦于模态弹窗交互、视觉层级设计、响应式布局等核心能力,同时为鸿蒙(HarmonyOS)跨端迁移提供了轻量化的技术映射范式。本文将从 React Native 核心实现逻辑切入,结合鸿蒙 ArkTS 技术体系,剖析轻量级页面的跨端开发思路与落地策略。

1. 状态管理与交互逻辑设计

该页面采用 React 核心的 useState 实现极简且高效的状态管理,针对公告详情页的核心交互场景,仅维护两个关键状态:

  • detailVisible:布尔型状态,控制详情弹窗的显隐,是移动端模态交互的核心状态;
  • detailTitle:字符串型状态,存储弹窗标题,实现弹窗内容与触发源的动态关联。

状态驱动的交互逻辑遵循 React 声明式编程范式,例如弹窗的显隐完全由 detailVisible 状态控制,无需手动操作 DOM:

{detailVisible && (
  <View style={styles.detailOverlay}>
    {/* 弹窗内容 */}
  </View>
)}

交互方法的设计高度内聚,onMore 方法同时完成标题赋值和弹窗显示,onCloseDetail 方法则重置状态关闭弹窗,形成完整的交互闭环:

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

这种极简的状态管理模式非常适配轻量级页面,避免了冗余的状态库引入,同时为跨端迁移保留了清晰的逻辑脉络。

2. 视觉层级与样式系统设计

公告详情页的核心设计诉求是信息层级的清晰呈现,其样式系统通过多层级的视觉设计实现了信息的有效区分:

  • 页面基础层:通过 SafeAreaView 适配设备安全区域,#fffaf8 的背景色奠定暖色调视觉基调,符合社团类应用的亲和力定位;
  • 内容卡片层card 样式通过 borderRadiusshadow 系列属性构建卡片化容器,与页面背景形成视觉隔离,shadowOpacity: 0.08 控制阴影透明度,保证 iOS/安卓视觉一致性;
  • 交互元素层:按钮类元素(moreBtnactionBtndetailBtn)采用差异化的背景色(#ffedd5)和文字色(#b45309)形成视觉焦点,同时通过 paddingborderRadius 保证触控区域大小符合移动端交互规范;
  • 模态弹窗层detailOverlay 采用绝对定位(position: 'absolute')+ 半透明背景(rgba(0,0,0,0.25))构建遮罩层,detailPanel 通过 maxWidth: 420 限制弹窗宽度,适配平板/手机等不同尺寸设备。

样式编写采用 StyleSheet.create 集中管理,属性命名遵循 React Native 驼峰命名规范(如 backgroundColor 而非 background-color),同时通过内联样式({ marginTop: 12 })补充局部样式,平衡样式复用性与灵活性。

3. 组件化与原生能力调用

页面采用“基础组件 + 自定义交互”的轻量化组件架构,核心组件的使用贴合 React Native 最佳实践:

  • Image 组件:使用 Base64 编码的图标资源,避免网络请求,提升页面加载速度,同时适配不同设备的分辨率;
  • TouchableOpacity:替代原生按钮组件,通过透明度变化提供点击反馈,相比 TouchableHighlight 更适配暖色调的视觉风格;
  • ScrollView:包裹核心内容区域,保证长文本场景下的滚动体验,无需引入更复杂的 FlatList
  • Alert.alert:封装原生弹窗能力,一行代码实现跨平台的操作反馈,替代 iOS 的 UIAlertController 和安卓的 AlertDialog

组件的组合方式遵循“单一职责”原则,例如将弹窗内容封装为独立的视图结构,与主内容区解耦,便于后续的跨端迁移和功能扩展。

1. 核心技术体系映射

轻量级页面的跨端适配核心在于“最小化改动,最大化复用”,React Native 与鸿蒙 ArkTS 的核心能力映射如下:

React Native 核心能力 鸿蒙 ArkTS 对应实现 适配要点
函数式组件 + useState @Component + @State/@Link 状态逻辑完全复用,仅需将 useState 替换为 @State 装饰器
JSX 声明式 UI TSX 声明式 UI 语法几乎完全兼容,组件名映射(ViewColumn/RowTextTextImageImageTouchableOpacityButton/Text+点击事件)
StyleSheet 样式系统 @Styles/@Extend 样式 Flex 布局属性完全复用,绝对定位改为 position: Position.FIXED,阴影属性调整为 shadow 统一配置
模态弹窗(条件渲染) if/else 条件渲染 + Stack 布局 保留 detailVisible 状态控制显隐,遮罩层改为 Stack 组件实现层级覆盖
Alert 弹窗 promptAction 弹窗 封装统一的弹窗工具函数,屏蔽平台 API 差异

2. 核心模块迁移实操示例

以模态弹窗模块为例,React Native 代码迁移到鸿蒙 ArkTS 的核心改动如下:

React Native 原代码

{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>
  </View>
)}

鸿蒙 ArkTS 迁移后代码

@Entry
@Component
struct ClubAnnouncementDetail {
  @State detailVisible: boolean = false;
  @State detailTitle: string | null = null;

  build() {
    Stack({ alignContent: Alignment.Center }) {
      // 主内容区
      Column() {
        // 头部、公告卡片、操作栏等内容
      }
      .width('100%')
      .height('100%')
      .backgroundColor('#fffaf8');

      // 模态弹窗
      if (this.detailVisible) {
        Column() {
          Column() {
            // 弹窗头部
            Row() {
              Text(this.detailTitle || '')
                .fontSize(16)
                .fontWeight(FontWeight.Bold)
                .color('#0f172a');
              Text('关闭')
                .fontSize(12)
                .color('#b45309')
                .backgroundColor('#ffedd5')
                .padding({ left: 8, right: 8, top: 4, bottom: 4 })
                .borderRadius(10)
                .onClick(() => {
                  this.detailVisible = false;
                  this.detailTitle = null;
                });
            }
            .width('100%')
            .justifyContent(FlexAlign.SpaceBetween);
            
            // 弹窗内容与底部按钮
            // ...
          }
          .width('100%')
          .maxWidth(420)
          .backgroundColor('#ffffff')
          .borderRadius(14)
          .padding(14)
          .shadow({ radius: 4, color: '#000', opacity: 0.12, offsetX: 0, offsetY: 2 });
        }
        .width('100%')
        .height('100%')
        .backgroundColor('rgba(0,0,0,0.25)')
        .justifyContent(FlexAlign.Center)
        .padding(16);
      }
    }
  }
}

核心改动点:

  • 将 React 函数式组件改为鸿蒙 @Component 装饰器的结构化组件,useState 替换为 @State 状态装饰器;
  • 模态弹窗的绝对定位改为鸿蒙 Stack 布局实现层级覆盖,保留原有的显隐控制逻辑;
  • 样式从 StyleSheet 内联改为链式调用,属性名(如 justifyContent)和值(如 SpaceBetween)基本复用;
  • TouchableOpacity 替换为 Text + onClick 事件,保留原有的点击反馈样式。
(1)业务逻辑完全复用

轻量级页面的核心价值在于交互逻辑,应将纯 TypeScript 编写的交互方法封装为独立工具函数,脱离框架依赖:

// 独立的交互逻辑文件 announcementLogic.ts
export const handleShowDetail = (title: string, setDetailTitle: (title: string | null) => void, setDetailVisible: (visible: boolean) => void) => {
  setDetailTitle(title);
  setDetailVisible(true);
};

export const handleCloseDetail = (setDetailTitle: (title: string | null) => void, setDetailVisible: (visible: boolean) => void) => {
  setDetailVisible(false);
  setDetailTitle(null);
};

export const handleShareAnnouncement = () => {
  return '已分享本条公告给社团成员';
};

export const handlePinAnnouncement = () => {
  return '已置顶公告到社团首页';
};

React Native 中调用:

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

鸿蒙中调用:

onMore(title: string) {
  this.detailTitle = title;
  this.detailVisible = true;
}
(2)样式常量

将页面的核心样式常量(颜色、圆角、间距)抽离为独立文件,实现跨端样式统一管理:

// styles/constants.ts
export const COLORS = {
  primary: '#b45309',
  primaryBg: '#ffedd5',
  secondaryBg: '#f1f5f9',
  textPrimary: '#0f172a',
  textSecondary: '#64748b',
  background: '#fffaf8',
};

export const SIZES = {
  borderRadius: 12,
  borderRadiusSmall: 10,
  paddingBase: 16,
  paddingSmall: 14,
  shadowOpacity: 0.08,
};

React Native 中使用:

card: { 
  backgroundColor: '#fff', 
  borderRadius: SIZES.borderRadius, 
  padding: SIZES.paddingSmall 
}

鸿蒙中使用:

.Column()
  .backgroundColor('#fff')
  .borderRadius(SIZES.borderRadius)
  .padding(SIZES.paddingSmall);
(3)原生能力

轻量级页面常需调用弹窗、分享等原生能力,封装统一的适配层可大幅降低跨端适配成本:

// utils/nativeAdapter.ts
export const showAlert = (title: string, message: string) => {
  if (typeof Alert !== 'undefined') {
    // React Native 环境
    Alert.alert(title, message);
  } else if (typeof promptAction !== 'undefined') {
    // 鸿蒙环境
    promptAction.showAlert({ title, message });
  }
};

三、轻量级页面

社团公告详情页作为典型的轻量级页面,其跨端开发的核心在于“极简适配,逻辑复用”:

  1. 状态逻辑 100% 复用:轻量级页面的状态管理通常仅依赖基础的显隐、数据赋值等逻辑,这些逻辑与框架无关,可完全复用;
  2. UI 层最小化改动:React Native 与鸿蒙的声明式 UI 语法高度相似,仅需替换组件名和样式写法,核心的布局逻辑(Flex)、视觉层级可完全保留;
  3. 原生能力抽象封装:轻量级页面的原生能力调用通常集中在弹窗、分享等基础能力,通过简单的适配层即可屏蔽平台差异;
  4. 样式系统统一管理:将颜色、间距、圆角等设计常量抽离,可保证跨端视觉风格的一致性,同时降低样式维护成本。

对于此类轻量级页面,React Native 代码迁移到鸿蒙的成本约为 10%-20%,其中 80% 以上的核心逻辑可直接复用,仅需适配 UI 组件和样式写法。

本文以 React Native 社团公告详情页为例,拆解了轻量级页面的核心实现逻辑,并深入分析了向鸿蒙跨端迁移的技术路径。核心要点可总结为:

  • React Native 端的核心价值在于极简的状态管理、清晰的视觉层级设计、轻量化的组件组合,为跨端迁移奠定了良好基础;
  • 鸿蒙端的适配核心是组件映射、样式调整、原生能力封装,核心交互逻辑可 100% 复用;
  • 轻量级页面跨端开发的关键是“抽离样式常量、封装原生能力、保留核心逻辑”,实现“极低改造成本,极高复用率”。

在移动应用开发中,公告详情这类看似简单的功能模块往往蕴含着复杂的状态管理和交互设计考量。ClubAnnouncementDetail组件通过精巧的状态控制机制,展示了如何在一个有限的空间内实现丰富的信息展示和用户交互。从技术架构层面来看,这个组件不仅体现了React Native开发的最佳实践,更在无形中为我们揭示了跨平台组件设计的重要原则。

这个组件的核心价值在于其"轻量级模态"的设计理念——通过局部状态控制实现类似模态弹窗的交互效果,避免了传统全屏模态带来的视觉打断。这种设计在社团管理、社交应用、内容展示等场景中具有广泛的适用性,而其技术实现方式在React Native和鸿蒙两大平台间也展现出高度的可移植性。

布尔状态

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

这种状态设计模式值得深入探讨。detailVisible控制组件的可见性,而detailTitle存储动态内容,两者分离确保了状态的最小化和可预测性。在跨端开发中,这种状态分离策略尤为重要——鸿蒙ArkUI的@State装饰器同样支持类似的细粒度状态管理,我们可以将这种模式映射为:

// 鸿蒙ArkTS等效实现
@State detailVisible: boolean = false;
@State detailTitle: string | null = null;

状态更新的原子性保证

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

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

这里的状态更新采用了显式的配对模式:onMore同时设置标题和可见性,onCloseDetail同时重置两者。这种设计避免了状态不一致的风险,在React Native的异步渲染模型和鸿蒙的声明式UI框架中都能保证可靠的UI状态同步。

条件渲染与绝对定位的结合

{detailVisible && (
  <View style={styles.detailOverlay}>
    <View style={styles.detailPanel}>
      {/* 模态内容 */}
    </View>
  </View>
)}

这种条件渲染模式是React Native中实现模态交互的经典方式。detailOverlay使用绝对定位覆盖整个屏幕,而detailPanel作为主要内容容器居中显示。在鸿蒙ArkUI中,类似的实现可以使用Stack布局配合if条件渲染:

// 鸿蒙ArkUI等效结构
Stack() {
  // 主内容
  Column() {
    // ...
  }
  
  // 模态层 - 条件渲染
  if (this.detailVisible) {
    Column()
      .width('100%')
      .height('100%')
      .backgroundColor('rgba(0,0,0,0.25)')
      .justifyContent(FlexAlign.Center)
      .alignItems(HorizontalAlign.Center)
    {
      Column() {
        // 模态内容
      }
      .width('80%')
      .backgroundColor(Color.White)
      .borderRadius(14)
      .padding(14)
    }
  }
}

触摸事件

Overlay层设计为可点击关闭,但detailPanel内部的点击不会意外触发关闭。这种事件处理机制在React Native中通过TouchableOpacity的嵌套实现,在鸿蒙中可以通过Gesture处理类似的事件冒泡控制。

阴影

detailPanel: {
  shadowColor: '#000',
  shadowOffset: { width: 0, height: 2 },
  shadowOpacity: 0.12,
  shadowRadius: 4,
}

React Native的阴影系统需要同时考虑iOS的shadow和Android的elevation。在鸿蒙ArkUI中,阴影效果通过shadow属性统一实现:

// 鸿蒙阴影等效
.shadow({
  radius: 4,
  color: Color.Black,
  offsetX: 0,
  offsetY: 2,
  opacity: 0.12
})

颜色命名

actionBtnPrimary: { backgroundColor: '#ffedd5' },
actionTextPrimary: { color: '#b45309' },

组件中使用了大量语义化的颜色值,这种设计为主题切换和深色模式适配提供了基础。在鸿蒙开发中,我们可以将这些颜色值提取到resources/base/element/color.json中,实现真正的跨主题适配。

Base64编码

const ICONS_BASE64 = {
  megaphone: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...',
  // 其他图标
};

使用Base64内联图片的优势在于:

  1. 减少HTTP请求:适合小型图标资源
  2. 部署简单:无需额外的资源管理
  3. 加载同步:避免图片加载的闪烁效果

但这种方法也有明显局限:

  1. 体积膨胀:Base64编码会使文件大小增加约33%
  2. 缓存失效:无法利用浏览器的图片缓存机制
  3. 内存占用:大图片不适合使用此方式

在鸿蒙跨端开发中,建议采用更规范的资源管理方式:

// 鸿蒙资源引用方式
Image($r('app.media.megaphone'))

头部布局

header: {
  flexDirection: 'row',
  justifyContent: 'space-between',
  alignItems: 'center',
}

Flexbox布局在React Native和鸿蒙ArkUI中都得到完整支持,这为跨端布局提供了坚实的技术基础。鸿蒙中的等效实现:

Row() {
  Text('社团管理 · 公告详情')
    .fontSize(18)
    .fontWeight(FontWeight.Bold)
    .fontColor('#0f172a')
  
  Row() {
    Image($r('app.media.megaphone'))
      .width(24)
      .height(24)
    Text('📣')
      .fontSize(18)
      .margin({ left: 8 })
  }
  .alignItems(VerticalAlign.Center)
}
.justifyContent(FlexAlign.SpaceBetween)
.alignItems(VerticalAlign.Center)
.padding(16)
.backgroundColor(Color.White)

操作栏

actionBar: {
  flexDirection: 'row',
  justifyContent: 'space-between',
}
actionBtn: {
  flex: 1,
  // 其他样式
}

这种等分布局模式在移动端UI中极为常见。flex: 1确保两个按钮均分可用空间,marginRight: 10提供间距。在鸿蒙中,我们可以使用Flex布局的权重属性实现相同效果:

Row() {
  Button('分享公告')
    .flexWeight(1)
    .margin({ right: 10 })
  Button('置顶公告')
    .flexWeight(1)
}
.justifyContent(FlexAlign.SpaceBetween)

组件层映射策略

React Native组件 鸿蒙ArkUI组件 适配说明
View Column/Row/Stack 根据布局需求选择
Text Text 文本属性基本对应
Image Image 资源引用方式不同
TouchableOpacity Button + 点击态 需处理按压效果
ScrollView Scroll 滚动行为一致

状态管理映射

React Native的useState对应鸿蒙的@State装饰器,但需要注意两者在更新机制上的差异。React Native的状态更新是异步的,而鸿蒙的状态更新会触发同步的UI重渲染。

样式系统适配

建议建立统一的样式映射表,将React Native的样式对象转换为鸿蒙的样式方法调用。关键样式属性的映射关系:

React Native样式 鸿蒙样式方法
padding: 16 .padding(16)
backgroundColor: ‘#fff’ .backgroundColor(Color.White)
borderRadius: 12 .borderRadius(12)
flexDirection: ‘row’ .flexDirection(FlexDirection.Row)

条件渲染

detailVisible的条件渲染在React Native中会触发完整的子树重渲染。在鸿蒙ArkUI中,if条件渲染具有类似的性能特征,都需要注意避免频繁的显隐切换。

图片资源

Base64内联图片虽然方便,但在列表或频繁渲染的场景中可能成为性能瓶颈。建议在鸿蒙端使用资源文件配合合适的压缩策略。


React Native 的核心优势在于其组件化设计,使得一套代码能够在多平台(包括鸿蒙系统)上运行。本次解析的 ClubAnnouncementDetail 组件,展示了如何构建一个功能完整、交互流畅的社团公告详情页,并实现鸿蒙系统的无缝适配。

页面布局

页面采用了经典的移动端布局结构,从外到内依次为:

  • SafeAreaView:作为根容器,确保内容在不同设备的安全区域内显示,自动适配刘海屏、状态栏和底部导航栏。在鸿蒙系统中,React Native 会调用系统 API 获取安全区域信息,确保内容不被遮挡。

  • Header:页面头部,包含标题和图标,采用 Flexbox 布局实现水平排列。flexDirection: 'row'justifyContent: 'space-between' 是跨端布局的常用组合,能够在不同屏幕尺寸下保持一致的视觉效果。

  • ScrollView:主体内容滚动容器,处理长文本和复杂布局的滚动。在鸿蒙系统中,ScrollView 会映射为 ArkUI 的 scroll-view 组件,保持原生的滚动体验,包括惯性滚动和回弹效果。

  • Card:公告内容卡片,使用阴影效果增强视觉层次感。React Native 的阴影属性(shadowColorshadowOffsetshadowOpacityshadowRadius)在鸿蒙系统上会被转换为对应的阴影样式,确保跨平台视觉一致。

状态管理

页面包含一个条件渲染的模态框,用于显示公告详情。模态框的显示/隐藏通过两个状态变量控制:detailVisibledetailTitle。这种基于状态的条件渲染是 React 组件的核心特性,在鸿蒙系统上同样高效运行。

模态框采用绝对定位(position: 'absolute')覆盖整个屏幕,背景使用半透明黑色(rgba(0,0,0,0.25))营造层次感。在鸿蒙系统中,绝对定位会转换为 ArkUI 的 position: 'fixed',保持相同的视觉效果。模态框的过渡动画可以通过 React Native 的 Animated API 实现,在鸿蒙系统上也能流畅运行。

状态管理与交互逻辑实现

Hooks 状态管理

组件使用 useState Hook 管理模态框的显示状态和标题。状态更新通过 setState 方法触发,React 会自动重新渲染组件树中受影响的部分。这种状态管理方式简洁高效,适合中小型应用。

在鸿蒙系统中,React Native 的状态更新机制与原生应用类似,通过虚拟 DOM Diff 算法优化渲染性能,只更新变化的部分。对于复杂应用,可以考虑使用 Redux 或 MobX 等状态管理库,这些库在鸿蒙系统上也能正常工作。

交互事件

组件定义了多个交互函数,处理用户的各种操作:

  • 按钮点击事件:使用 TouchableOpacity 组件实现可点击区域,通过 onPress 属性绑定回调函数。在鸿蒙系统中,TouchableOpacity 会转换为具有按压效果的 ArkUI 组件,保持原生的触摸反馈。

  • Alert 弹窗:使用 Alert.alert 显示操作结果提示。在鸿蒙系统中,React Native 会调用系统原生的弹窗 API,确保弹窗样式与系统一致。

  • 模态框交互:通过 onMoreonCloseDetail 函数控制模态框的显示和隐藏,实现了流畅的交互体验。

StyleSheet

组件使用 StyleSheet.create 方法定义样式,将所有样式集中管理。这种方式的优势在于:

  1. 性能优化:StyleSheet 在编译时会被处理,减少运行时计算,提高渲染性能。

  2. 类型安全:TypeScript 会检查样式属性,减少运行时错误。

  3. 模块化:便于样式复用和主题管理,适合跨端开发。

在鸿蒙系统中,React Native 的样式会被转换为 ArkUI 的样式规则,例如:

  • flex: 1 转换为 flex-grow: 1
  • borderRadius: 12 转换为 border-radius: 12px
  • padding: 16 转换为 padding: 16px

主题

页面采用了统一的主题色彩,主色调为暖橙色系,通过不同的颜色深浅营造层次感。这种主题设计在跨端开发中非常重要,能够确保应用在不同平台上具有一致的品牌形象。

样式定义遵循了清晰的命名规范,例如:

  • actionBtn 用于普通按钮
  • actionBtnPrimary 用于主要按钮,通过样式继承实现样式复用

这种命名方式便于维护和扩展,同时提高了代码的可读性。

React Native 到鸿蒙 ArkUI 的组件映射

React Native 组件到鸿蒙 ArkUI 组件的映射是跨端适配的核心机制。以下是主要组件的映射关系:

React Native 组件 鸿蒙 ArkUI 组件 说明
SafeAreaView Stack 安全区域容器
View Div 基础容器组件
Text Text 文本组件
ScrollView ScrollView 滚动容器
TouchableOpacity Button 可点击组件
Image Image 图片组件
Alert AlertDialog 弹窗组件

这种映射机制确保了 React Native 组件在鸿蒙系统上的原生表现,同时保持了开发体验的一致性。

组件渲染

组件采用了条件渲染({detailVisible && (...)})来控制模态框的显示,只有当条件为真时才会渲染模态框内容。这种方式能够减少不必要的 DOM 节点,提高渲染性能。

在鸿蒙系统中,React Native 的条件渲染同样高效,只在状态变化时更新 DOM 结构,避免了全量重渲染。

状态管理

组件使用了最小化状态设计,只管理必要的状态变量(detailVisibledetailTitle),避免了状态冗余。这种设计方式便于维护和调试,同时提高了组件的复用性。

在跨端开发中,合理的状态管理至关重要,能够减少平台特定代码,提高代码的可移植性。

样式

组件使用了 StyleSheet.create 定义样式,避免了内联样式的性能问题。同时,通过样式继承实现样式复用,减少了代码冗余。

在鸿蒙系统中,StyleSheet 定义的样式会被编译为高效的样式规则,提高渲染性能。

React Native 鸿蒙跨端开发为开发者提供了一种高效的解决方案,能够使用一套代码构建出在多平台上表现一致的高质量应用。本次解析的社团公告详情页,展示了如何利用 React Native 的组件化设计、状态管理和样式系统,构建一个功能完整、交互流畅的页面,并实现鸿蒙系统的无缝适配。


真实演示案例代码:

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

const ICONS_BASE64 = {
  megaphone: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNgYAAAAAMAASsJTYQAAAAASUVORK5CYII=',
  pin: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNgYAAAAAMAASsJTYQAAAAASUVORK5CYII=',
  open: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNgYAAAAAMAASsJTYQAAAAASUVORK5CYII=',
};

const ClubAnnouncementList: React.FC = () => {
  const [detailVisible, setDetailVisible] = useState(false);
  const [detailTitle, setDetailTitle] = useState<string | null>(null);
  const onOpen = (title: string) => {
    setDetailTitle(title);
    setDetailVisible(true);
  };
  const onPin = (title: string) => Alert.alert('置顶', `已置顶:${title}`);
  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.megaphone }} style={styles.headerIconImg} />
          <Text style={styles.headerEmoji}>📣</Text>
        </View>
      </View>

      <ScrollView style={styles.content}>
        <View style={styles.card}>
          <Text style={styles.annTitle}>春季团建活动安排</Text>
          <Text style={styles.annMeta}>2026-02-01 · 社团秘书处</Text>
          <View style={styles.actionsRow}>
            <TouchableOpacity style={styles.actionBtn} onPress={() => onOpen('春季团建活动安排')}>
              <Image source={{ uri: ICONS_BASE64.open }} style={styles.actionIcon} />
              <Text style={styles.actionText}>详情</Text>
            </TouchableOpacity>
            <TouchableOpacity style={[styles.actionBtn, styles.actionBtnPrimary]} onPress={() => onPin('春季团建活动安排')}>
              <Image source={{ uri: ICONS_BASE64.pin }} style={styles.actionIcon} />
              <Text style={styles.actionTextPrimary}>置顶</Text>
            </TouchableOpacity>
          </View>
        </View>
        <View style={styles.card}>
          <Text style={styles.annTitle}>年度会员招募</Text>
          <Text style={styles.annMeta}>2026-01-28 · 组织部</Text>
          <View style={styles.actionsRow}>
            <TouchableOpacity style={styles.actionBtn} onPress={() => onOpen('年度会员招募')}>
              <Image source={{ uri: ICONS_BASE64.open }} style={styles.actionIcon} />
              <Text style={styles.actionText}>详情</Text>
            </TouchableOpacity>
            <TouchableOpacity style={[styles.actionBtn, styles.actionBtnPrimary]} onPress={() => onPin('年度会员招募')}>
              <Image source={{ uri: ICONS_BASE64.pin }} style={styles.actionIcon} />
              <Text style={styles.actionTextPrimary}>置顶</Text>
            </TouchableOpacity>
          </View>
        </View>
      </ScrollView>
      {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.megaphone }} style={styles.detailIcon} />
                <Text style={styles.detailText}>公告正文与报名方式说明。</Text>
              </View>
              <View style={styles.detailRow}>
                <Image source={{ uri: ICONS_BASE64.pin }} 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: '#fffefc' },
  header: { padding: 16, backgroundColor: '#ffffff', borderBottomWidth: 1, borderBottomColor: '#fde68a', 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 },
  card: { backgroundColor: '#ffffff', borderRadius: 12, padding: 12, marginBottom: 12, borderWidth: 1, borderColor: '#e2e8f0' },
  annTitle: { fontSize: 14, fontWeight: '600', color: '#0f172a' },
  annMeta: { fontSize: 12, color: '#64748b', marginTop: 4 },
  actionsRow: { flexDirection: 'row', marginTop: 8 },
  actionBtn: { flexDirection: 'row', alignItems: 'center', backgroundColor: '#f1f5f9', borderRadius: 10, paddingVertical: 8, paddingHorizontal: 12, marginRight: 8 },
  actionBtnPrimary: { backgroundColor: '#ffedd5' },
  actionIcon: { width: 16, height: 16, marginRight: 6 },
  actionText: { fontSize: 12, color: '#334155', fontWeight: '500' },
  actionTextPrimary: { fontSize: 12, color: '#b45309', 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: '#b45309', backgroundColor: '#ffedd5', 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: '#ffedd5' },
  detailBtnText: { fontSize: 12, color: '#334155', fontWeight: '600' },
  detailBtnTextPrimary: { fontSize: 12, color: '#b45309', fontWeight: '700' },
});

export default ClubAnnouncementList;

请添加图片描述


打包

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

在这里插入图片描述

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

在这里插入图片描述

最后运行效果图如下显示:
请添加图片描述
本文探讨了社团公告详情页的React Native实现与鸿蒙(HarmonyOS)跨端迁移方案。通过极简状态管理(useState)处理弹窗交互,采用分层视觉设计确保信息层级清晰,并运用组件化架构实现原生能力调用。重点分析了React Native与鸿蒙ArkTS的技术映射关系,包括状态管理、UI声明、样式系统等核心能力的转换策略,并以模态弹窗模块为例展示了具体迁移过程。该方案为轻量级页面提供了高效的跨端开发思路,实现最小化改动、最大化复用的目标。

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

Logo

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

更多推荐