React Native鸿蒙跨平台实现取件码生成、包裹状态流转、搜索筛选、多步表单交互,实现了基于快递单号、收件人姓名、取件码的多维度搜索筛选
本文介绍了基于React Native开发的驿站包裹管理应用如何适配鸿蒙系统。应用采用React函数式组件+TypeScript技术栈,通过强类型数据模型确保跨端一致性,使用useState轻量级状态管理实现多端状态同步。UI层深度复刻鸿蒙设计规范,采用Flex布局适配不同设备尺寸,并实现鸿蒙风格的视觉样式和交互组件。核心业务逻辑如取件码生成、状态流转和搜索筛选均采用纯JavaScript实现,天
欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。
在末端物流数字化升级的进程中,驿站管理类应用需要同时满足多终端适配、轻量化操作、状态实时同步等核心诉求。React Native 凭借其“一次开发,多端部署”的技术特性,成为连接 iOS、Android 与鸿蒙(HarmonyOS)系统的最优技术路径。本文以驿站包裹管理应用为例,从数据模型设计、鸿蒙风格 UI 实现、业务逻辑跨端兼容等维度,完整解析 React Native 对接鸿蒙系统的技术内核与落地实践。
一、基础架构
本驿站管理应用聚焦包裹到站、通知、取件全流程管理,核心覆盖“待取件-已通知-已取件”三大状态流转,整体架构遵循 React Native 组件化开发范式,同时深度契合鸿蒙系统的设计语言与交互逻辑。从技术选型来看,应用基于 React 函数式组件 + TypeScript 构建,这种组合既保证了代码的类型安全,又能最大化跨端复用率,是 React Native 适配鸿蒙系统的理想技术底座。
1. 跨端数据一致性
在跨端应用开发中,统一且精准的数据模型是避免多端行为不一致的关键前提。代码中通过 TypeScript 严格定义了包裹的核心数据结构,覆盖驿站管理全场景的业务属性:
// 包裹核心数据模型
type Parcel = {
id: string;
trackingNumber: string;
recipientName: string;
recipientPhone: string;
arrivalTime: string;
pickupCode: string;
status: '待取件' | '已通知' | '已取件';
};
这种强类型定义不仅在开发阶段提供语法校验和智能提示,更关键的是在鸿蒙系统适配时,能够与 ArkTS 的类型系统形成天然映射。相较于纯 JavaScript 开发,TypeScript 可有效规避因数据类型模糊导致的跨端兼容性问题——尤其是在鸿蒙这类面向全场景智慧终端的操作系统中,类型安全能大幅降低多设备适配的调试成本,确保包裹状态、取件码等核心数据在不同终端的一致性。
2. 轻量级状态管理:
应用采用 React 内置的 useState Hook 管理核心业务状态,结合不可变数据模式实现包裹状态的跨端更新:
const [parcels, setParcels] = useState<Parcel[]>([...]);
const [searchTerm, setSearchTerm] = useState('');
// 状态更新示例 - 标记包裹已取件
setParcels(parcels.map(p =>
p.id === parcelId
? { ...p, status: '已取件' }
: p
));
这种轻量级状态管理方案完全适配跨端开发场景,相较于 Redux 等重型状态库,useState 无需额外的中间件和适配层,能够直接在 React Native 支持的所有平台(包括鸿蒙)上稳定运行。从鸿蒙系统的视角来看,useState 的状态更新逻辑与 ArkUI 的 @State 装饰器在设计理念上高度契合,开发者无需切换思维模式即可完成跨端开发,大幅降低了鸿蒙适配的学习成本。
React Native 的核心价值在于通过统一的组件抽象层,屏蔽不同平台的 UI 实现差异。本应用在 UI 层的设计深度复刻了鸿蒙系统的视觉风格与交互规范,同时保证多端体验的一致性。
1. Flex 布局
应用基于 React Native 的 Flex 布局系统构建整体界面,通过 Dimensions.get('window') 获取设备宽高,实现对不同尺寸鸿蒙设备(手机、平板、桌面终端)的自适应:
const { width, height } = Dimensions.get('window');
相较于鸿蒙系统的 DirectionalLayout 和 StackLayout,React Native 的 Flex 布局具备更强的跨端兼容性,通过 flexDirection、justifyContent、flexWrap 等属性,能够精准还原鸿蒙系统的布局逻辑。例如包裹操作按钮区域的自适应布局:
<View style={styles.parcelActions}>
{parcel.status === '待取件' && (
<>
<TouchableOpacity style={styles.editButton}>
<Text style={styles.editButtonText}>编辑信息</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.notifyButton}>
<Text style={styles.notifyButtonText}>发送通知</Text>
</TouchableOpacity>
</>
)}
{/* 其他状态按钮 */}
</View>
这段代码通过 flexDirection: 'row' 和 flexWrap: 'wrap' 实现了鸿蒙系统特有的流式布局效果,在不同尺寸的鸿蒙设备上都能保持按钮的合理排列,无需针对鸿蒙系统做额外的布局适配。
2. 视觉样式:
应用通过 StyleSheet.create 定义样式表,深度适配鸿蒙系统的设计规范,核心体现在以下几个维度:
- 色彩体系:采用鸿蒙系统的暖黄色系主色调(
#ca8a04、#854d0e),搭配功能性色彩区分包裹状态(待取件-橙色、已通知-蓝色、已取件-绿色),符合鸿蒙系统“自然、舒适”的视觉设计理念 - 圆角与阴影:使用
borderRadius: 12实现鸿蒙风格的大圆角设计,通过elevation(Android)和shadow(iOS)属性适配鸿蒙系统的阴影效果,兼顾视觉层次感与跨端一致性 - 状态徽章与取件码样式:通过动态样式绑定实现鸿蒙风格的状态标签,同时强化取件码的视觉层级:
<View style={[
styles.statusBadge,
{ backgroundColor: getStatusColor(parcel.status) }
]}>
<Text style={styles.statusText}>{parcel.status}</Text>
</View>
<Text style={styles.pickupCode}>{parcel.pickupCode}</Text>
这种样式设计方案完全基于 React Native 的标准 API 实现,在鸿蒙系统中能够通过 React Native 的渲染层自动转换为原生样式——backgroundColor 对应鸿蒙的 background-color,borderRadius 对应鸿蒙的 border-radius,无需编写平台特定代码。
3. 交互组件:
应用中所有交互组件均基于 React Native 基础组件封装,同时适配鸿蒙系统的交互规范,新增的 TextInput 组件更是实现了鸿蒙风格的搜索与表单交互:
- SafeAreaView:对应鸿蒙系统的
SafeArea组件,适配刘海屏、挖孔屏等异形屏,保证界面在鸿蒙不同终端设备上的完整性 - TextInput:替代鸿蒙的
Input组件,实现搜索框与表单输入功能,通过placeholder、onChangeText等属性适配鸿蒙的输入交互规范 - Alert.prompt:对应鸿蒙的
TextInputDialog组件,实现包裹添加、收件人信息编辑等带输入的弹窗交互,保持与鸿蒙原生应用一致的交互体验 - TouchableOpacity:替代鸿蒙的
Button组件,实现点击反馈效果,符合鸿蒙的交互规范
以添加包裹的交互流程为例,代码通过 Alert.prompt 实现鸿蒙风格的分步输入弹窗:
Alert.prompt(
'添加到站包裹',
'请输入快递单号:',
[
{ text: '取消', style: 'cancel' },
{
text: '确定',
onPress: (trackingNumber) => {
if (trackingNumber) {
// 包裹添加逻辑
Alert.alert('成功', `包裹已添加,取件码: ${newParcel.pickupCode}`);
}
}
}
],
'plain-text'
);
这种交互逻辑完全基于 React Native 的跨端 API 实现,在鸿蒙系统中能够保持与原生应用一致的交互体验,无需针对鸿蒙系统做特殊处理。
除了 UI 层的适配,业务逻辑的跨端兼容性是 React Native 开发的核心。本应用的核心业务逻辑包括取件码生成、包裹状态流转、搜索筛选、多步表单交互等,这些逻辑完全基于 JavaScript/TypeScript 实现,天然具备跨端运行能力。
1. 取件码生成与包裹状态流转
应用实现了完整的取件码自动生成逻辑,通过纯函数生成符合驿站规范的字母+数字组合取件码:
const generatePickupCode = () => {
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
const numbers = '0123456789';
let code = '';
// 生成1个字母 + 3个数字的取件码
code += letters.charAt(Math.floor(Math.random() * letters.length));
for (let i = 0; i < 3; i++) {
code += numbers.charAt(Math.floor(Math.random() * numbers.length));
}
return code;
};
该函数完全基于纯 JavaScript 实现,不依赖任何平台特定 API,在 React Native 支持的所有平台(包括鸿蒙)上都能稳定运行。同时,包裹状态流转逻辑(待取件 → 已通知 → 已取件)采用不可变数据模式更新,通过 Array.map 实现状态的跨端一致性更新,避免因平台运行时差异导致的状态错乱。
2. 多维度搜索筛选
应用实现了基于快递单号、收件人姓名、取件码的多维度搜索筛选:
const filteredParcels = parcels.filter(parcel =>
parcel.trackingNumber.includes(searchTerm) ||
parcel.recipientName.includes(searchTerm) ||
parcel.pickupCode.includes(searchTerm)
);
这种无副作用的纯函数筛选方式,不仅符合 React 的设计理念,更重要的是在跨端场景下,能够避免因不同平台的运行时差异导致的筛选结果不一致。对于鸿蒙系统而言,这类纯逻辑代码无需任何适配即可直接运行,是跨端开发的最优实践。
3. 多步表单
应用针对收件人信息编辑场景,实现了鸿蒙风格的多步表单交互:
Alert.prompt(
'编辑收件人信息',
'请输入收件人姓名:',
[
{ text: '取消', style: 'cancel' },
{
text: '下一步',
onPress: (name) => {
if (name) {
Alert.prompt(
'编辑收件人信息',
'请输入收件人电话:',
[/* 确认逻辑 */],
'plain-text',
parcel.recipientPhone
);
}
}
}
],
'plain-text',
parcel.recipientName
);
该逻辑基于 React Native 的 Alert.prompt 嵌套实现,完全兼容鸿蒙系统的弹窗交互规范,在不同平台上都能保持一致的交互流程,体现了 React Native “一次编写,多端复用”的核心价值。
从本应用的实现来看,React Native 对接鸿蒙系统的核心在于“抽象层适配”,而非“重写适配”,具体体现在以下几个维度:
-
JS 引擎层兼容:鸿蒙系统内置了符合 ECMAScript 标准的 JavaScript 引擎,能够直接执行 React Native 的 JS 代码,这是跨端运行的底层基础。本应用中所有的业务逻辑代码(取件码生成、状态更新、搜索筛选)均运行在 JS 引擎层,无需任何修改即可在鸿蒙系统中执行,保证了业务逻辑的跨端一致性。
-
组件映射层适配:React Native 通过自定义渲染器,将 React 组件(View、Text、TextInput、TouchableOpacity 等)映射为鸿蒙系统的原生组件。例如
TextInput组件会被转换为鸿蒙的Input组件,Alert.prompt会被转换为鸿蒙的TextInputDialog组件,这种映射关系由 React Native 的鸿蒙适配层自动完成,开发者无需关注底层实现细节。 -
样式转换层适配:React Native 的 StyleSheet 样式会被自动转换为鸿蒙系统的原生样式,本应用中定义的所有鸿蒙风格样式(暖黄色系、大圆角、状态色彩)都能通过这一层完成自动转换,保证了 UI 风格在鸿蒙系统中的一致性。
-
API 适配层兼容:React Native 提供的基础 API(如 Alert、Dimensions、TextInput)都有对应的鸿蒙实现,本应用中使用的
Alert.prompt、TextInput等 API,在鸿蒙系统中会被替换为对应的原生 API 调用,保证了交互行为的跨端一致性。
本驿站管理应用的实现完整展现了 React Native 在鸿蒙跨端开发领域的技术优势,核心要点可总结为:
- 强类型设计保障跨端一致性:TypeScript 类型定义不仅提升代码质量,更能与鸿蒙 ArkTS 形成类型映射,降低跨端适配成本,是物流驿站类应用跨端开发的基础保障
- 纯逻辑开发最大化复用率:取件码生成、状态流转、搜索筛选等核心逻辑采用纯 JavaScript/TypeScript 实现,无需针对鸿蒙系统做特殊修改,大幅提升开发效率
- 标准 API 适配鸿蒙生态:基于 React Native 标准组件和 API 开发,通过底层适配层自动对接鸿蒙原生能力,兼顾开发效率与原生体验
对于希望基于 React Native 开发鸿蒙应用的开发者而言,本案例提供了清晰的实践路径:聚焦业务逻辑的跨端复用,依托 React Native 的抽象层完成与鸿蒙系统的对接,既能享受跨端开发的效率优势,又能保证鸿蒙系统的原生体验,是驿站管理类应用跨端开发的最优解。
本项目采用React Native函数式组件架构,以ParcelStationApp为核心组件,实现了驿站包裹管理的完整功能流程。架构设计遵循模块化原则,将数据结构、状态管理和业务逻辑清晰分离,便于维护和扩展。
核心技术栈
- React Native:跨平台移动应用开发框架,支持iOS、Android和鸿蒙系统
- TypeScript:提供类型安全,增强代码可维护性和开发体验
- Hooks API:使用useState进行状态管理,简化组件逻辑
- Flexbox:实现响应式布局,适配不同屏幕尺寸
- Base64图标:内置图标资源,减少网络请求,提升加载速度
- Dimensions API:获取屏幕尺寸,实现精细化布局控制
包裹类型(Parcel)
type Parcel = {
id: string;
trackingNumber: string;
recipientName: string;
recipientPhone: string;
arrivalTime: string;
pickupCode: string;
status: '待取件' | '已通知' | '已取件';
};
该类型设计全面,包含了包裹管理的核心信息:
id:唯一标识符,确保数据唯一性trackingNumber:快递单号,便于包裹追踪recipientName、recipientPhone:收件人信息,便于联系arrivalTime:到站时间,用于包裹管理pickupCode:取件码,确保包裹安全领取status:包裹状态,使用联合类型确保类型安全,清晰定义业务流程
核心状态
应用使用useState钩子管理两个核心状态:
parcels:包裹列表,支持动态更新searchTerm:搜索关键词,用于包裹查找
状态更新
- 包裹添加:通过handleAddParcel函数,生成新包裹并添加到列表
- 状态更新:通过handleNotifyRecipient和handleMarkPickedUp函数,更新包裹状态
- 信息编辑:通过handleEditRecipient函数,修改收件人信息
- 搜索功能:通过searchTerm状态,实现包裹查找
状态管理
- 不可变数据模式:使用扩展运算符(
...)创建新状态,避免直接修改原状态 - 批量更新:在状态更新时使用map方法批量处理,确保数据一致性
- 状态验证:在进行操作前,通过find方法验证包裹是否存在
- 用户反馈:操作完成后,通过Alert组件提供明确的成功提示
核心业务
- 包裹管理:展示包裹列表,包含完整的包裹信息
- 包裹添加:输入快递单号,自动生成取件码,添加新包裹
- 通知收件人:发送取件通知,更新包裹状态为"已通知"
- 确认取件:验证取件信息,更新包裹状态为"已取件"
- 信息编辑:修改收件人信息,确保数据准确性
核心业务
const handleAddParcel = () => {
Alert.prompt(
'添加到站包裹',
'请输入快递单号:',
[
{ text: '取消', style: 'cancel' },
{
text: '确定',
onPress: (trackingNumber) => {
if (trackingNumber) {
const newParcel: Parcel = {
id: (parcels.length + 1).toString(),
trackingNumber: trackingNumber,
recipientName: '待填写',
recipientPhone: '待填写',
arrivalTime: new Date().toLocaleString('zh-CN'),
pickupCode: generatePickupCode(),
status: '待取件'
};
setParcels([...parcels, newParcel]);
Alert.alert('成功', `包裹已添加,取件码: ${newParcel.pickupCode}`);
}
}
}
],
'plain-text'
);
};
数据流
- 单向数据流:状态 → 视图 → 用户操作 → 状态更新
- 数据验证:在添加包裹前验证快递单号是否存在
- 业务逻辑封装:将复杂操作逻辑封装在专用函数中,提高代码可读性
- 用户交互反馈:使用Alert组件提供操作确认和信息提示,提升用户体验
组件
- 核心组件:SafeAreaView、View、Text、ScrollView、TouchableOpacity、TextInput等在鸿蒙系统上有对应实现
- API兼容性:Dimensions API在鸿蒙系统中可正常使用,确保布局适配
- Alert组件:鸿蒙系统支持Alert组件的基本功能,但样式可能有所差异
- Alert.prompt:需要注意鸿蒙系统对Alert.prompt的支持情况,可能需要自定义实现
资源
- Base64图标:在鸿蒙系统中同样支持,可减少网络请求,提升性能
- 内存管理:鸿蒙系统对内存使用更为严格,需注意资源释放
- 计算性能:对于取件码生成等操作,鸿蒙系统的处理性能与其他平台相当
性能
-
渲染性能:
- 避免不必要的重渲染,合理使用React.memo
- 对于长包裹列表,建议使用FlatList替代ScrollView
- 优化组件结构,减少嵌套层级
-
数据处理:
- 缓存计算结果,避免重复计算
- 优化包裹查找算法,特别是搜索功能
- 考虑使用useMemo缓存计算结果,提高性能
-
内存管理:
- 及时释放不再使用的资源
- 避免内存泄漏,特别是在处理多个包裹时
- 合理使用缓存策略,平衡性能和内存占用
类型定义
- 使用枚举类型:将包裹状态使用枚举类型替代字符串,提高代码可读性和可维护性
- 类型扩展:考虑使用接口继承,增强类型系统的表达能力
- 类型守卫:添加更多类型守卫,确保运行时类型安全
- 国际化支持:考虑添加国际化支持,特别是包裹状态等术语
状态管理
- 状态分离:对于复杂状态,考虑使用useReducer或状态管理库(如Redux、Zustand)
- 状态持久化:实现状态持久化,使用AsyncStorage存储包裹数据
- 计算属性:使用useMemo缓存计算结果,避免重复计算
- 批量操作:支持批量管理包裹,提高驿站效率
组件化
- 组件拆分:将大型组件拆分为更小的可复用组件,如ParcelCard、AddParcelForm、SearchBar等
- 自定义Hook:提取重复的状态逻辑到自定义Hook,如useParcelManager、usePickupCodeGenerator等
- 高阶组件:使用高阶组件处理横切关注点,如错误边界、加载状态等
- 表单组件:封装表单输入组件,提高代码复用性
业务逻辑
- 服务层分离:将业务逻辑分离到服务层,提高代码可测试性
- 错误处理:增强错误处理机制,提供更友好的错误提示
- 实时更新:考虑添加实时包裹状态更新,提升用户体验
- 智能排序:实现包裹智能排序,如按到站时间、状态等排序
性能
- 列表优化:使用FlatList的性能优化特性,如getItemLayout、initialNumToRender等
- 网络优化:实现请求防抖和节流,减少网络请求频率
- Bundle优化:使用代码分割和Tree Shaking,减少应用体积
- 启动优化:优化应用启动速度,减少初始加载时间
本项目展示了如何使用React Native和TypeScript构建一个功能完整的驿站管理应用。通过合理的架构设计、类型定义和状态管理,实现了跨平台的一致性体验。
在鸿蒙跨端开发方面,需要注意平台特定API的兼容性处理,优化资源管理和渲染性能,确保在不同平台上都能提供良好的用户体验。同时,由于涉及包裹管理的特殊性,还需特别关注数据准确性和操作效率。
随着React Native和鸿蒙系统的不断发展,跨端开发将会变得更加成熟和高效。未来可以考虑:
- 使用React Native 0.70+版本:利用新的架构特性,如Fabric渲染器和Turbo Modules,提升应用性能
- 探索鸿蒙原生能力:充分利用鸿蒙系统的分布式能力,实现多设备协同和更丰富的功能
- 引入现代化状态管理:使用Redux Toolkit或Zustand等现代状态管理库,简化状态管理
- 实现PWA支持:扩展应用的使用场景,支持Web平台
- 集成AI能力:引入AI技术,如包裹自动识别、智能分拣等,提升应用智能化水平
- 实时监控系统:集成实时监控系统,实现对驿站运营状态的24小时监控
- 多语言支持:添加多语言支持,提升应用的全球适配能力
真实演示案例代码:
// App.tsx
import React, { useState } from 'react';
import { SafeAreaView, View, Text, StyleSheet, TouchableOpacity, ScrollView, Dimensions, Alert, TextInput } from 'react-native';
// Base64 图标库
const ICONS_BASE64 = {
package: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
inbox: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
notification: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
code: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
phone: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
search: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
check: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
info: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
};
const { width, height } = Dimensions.get('window');
// 包裹类型
type Parcel = {
id: string;
trackingNumber: string;
recipientName: string;
recipientPhone: string;
arrivalTime: string;
pickupCode: string;
status: '待取件' | '已通知' | '已取件';
};
// 驿站管理应用组件
const ParcelStationApp: React.FC = () => {
const [parcels, setParcels] = useState<Parcel[]>([
{
id: '1',
trackingNumber: 'YT123456789CN',
recipientName: '张三',
recipientPhone: '138****1234',
arrivalTime: '2023-12-01 10:30',
pickupCode: 'A123',
status: '待取件'
},
{
id: '2',
trackingNumber: 'SF987654321CN',
recipientName: '李四',
recipientPhone: '139****5678',
arrivalTime: '2023-12-01 11:15',
pickupCode: 'B456',
status: '已通知'
},
{
id: '3',
trackingNumber: 'JD555666777CN',
recipientName: '王五',
recipientPhone: '137****9012',
arrivalTime: '2023-12-01 09:45',
pickupCode: 'C789',
status: '已取件'
}
]);
const [searchTerm, setSearchTerm] = useState('');
const getStatusColor = (status: string) => {
switch (status) {
case '待取件': return '#f59e0b';
case '已通知': return '#3b82f6';
case '已取件': return '#10b981';
default: return '#6b7280';
}
};
const generatePickupCode = () => {
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
const numbers = '0123456789';
let code = '';
// 生成1个字母 + 3个数字的取件码
code += letters.charAt(Math.floor(Math.random() * letters.length));
for (let i = 0; i < 3; i++) {
code += numbers.charAt(Math.floor(Math.random() * numbers.length));
}
return code;
};
const handleAddParcel = () => {
Alert.prompt(
'添加到站包裹',
'请输入快递单号:',
[
{ text: '取消', style: 'cancel' },
{
text: '确定',
onPress: (trackingNumber) => {
if (trackingNumber) {
const newParcel: Parcel = {
id: (parcels.length + 1).toString(),
trackingNumber: trackingNumber,
recipientName: '待填写',
recipientPhone: '待填写',
arrivalTime: new Date().toLocaleString('zh-CN'),
pickupCode: generatePickupCode(),
status: '待取件'
};
setParcels([...parcels, newParcel]);
Alert.alert('成功', `包裹已添加,取件码: ${newParcel.pickupCode}`);
}
}
}
],
'plain-text'
);
};
const handleNotifyRecipient = (parcelId: string) => {
const parcel = parcels.find(p => p.id === parcelId);
if (parcel) {
Alert.alert(
'通知收件人',
`快递单号: ${parcel.trackingNumber}\n` +
`收件人: ${parcel.recipientName}\n` +
`取件码: ${parcel.pickupCode}\n\n` +
`确定发送取件通知吗?`,
[
{ text: '取消', style: 'cancel' },
{
text: '发送通知',
onPress: () => {
setParcels(parcels.map(p =>
p.id === parcelId
? { ...p, status: '已通知' }
: p
));
Alert.alert('成功', '取件通知已发送');
}
}
]
);
}
};
const handleMarkPickedUp = (parcelId: string) => {
const parcel = parcels.find(p => p.id === parcelId);
if (parcel) {
Alert.alert(
'确认取件',
`快递单号: ${parcel.trackingNumber}\n` +
`取件码: ${parcel.pickupCode}\n\n` +
`确认包裹已被取走吗?`,
[
{ text: '取消', style: 'cancel' },
{
text: '确认取件',
onPress: () => {
setParcels(parcels.map(p =>
p.id === parcelId
? { ...p, status: '已取件' }
: p
));
Alert.alert('成功', '包裹状态已更新为已取件');
}
}
]
);
}
};
const handleEditRecipient = (parcelId: string) => {
const parcel = parcels.find(p => p.id === parcelId);
if (parcel) {
Alert.prompt(
'编辑收件人信息',
'请输入收件人姓名:',
[
{ text: '取消', style: 'cancel' },
{
text: '下一步',
onPress: (name) => {
if (name) {
Alert.prompt(
'编辑收件人信息',
'请输入收件人电话:',
[
{ text: '取消', style: 'cancel' },
{
text: '确定',
onPress: (phone) => {
if (phone) {
setParcels(parcels.map(p =>
p.id === parcelId
? { ...p, recipientName: name, recipientPhone: phone }
: p
));
Alert.alert('成功', '收件人信息已更新');
}
}
}
],
'plain-text',
parcel.recipientPhone
);
}
}
}
],
'plain-text',
parcel.recipientName
);
}
};
const filteredParcels = parcels.filter(parcel =>
parcel.trackingNumber.includes(searchTerm) ||
parcel.recipientName.includes(searchTerm) ||
parcel.pickupCode.includes(searchTerm)
);
return (
<SafeAreaView style={styles.container}>
{/* 头部 */}
<View style={styles.header}>
<Text style={styles.title}>驿站管理</Text>
<Text style={styles.subtitle}>到站包裹管理与通知</Text>
</View>
<ScrollView style={styles.content}>
{/* 搜索和添加 */}
<View style={styles.searchCard}>
<View style={styles.searchContainer}>
<Text style={styles.searchIcon}>🔍</Text>
<TextInput
style={styles.searchInput}
placeholder="搜索单号、姓名或取件码"
value={searchTerm}
onChangeText={setSearchTerm}
/>
</View>
<TouchableOpacity
style={styles.addButton}
onPress={handleAddParcel}
>
<Text style={styles.addButtonText}>+ 添加包裹</Text>
</TouchableOpacity>
</View>
{/* 包裹统计 */}
<View style={styles.statsCard}>
<View style={styles.statItem}>
<Text style={styles.statNumber}>
{parcels.filter(p => p.status === '待取件').length}
</Text>
<Text style={styles.statLabel}>待取件</Text>
</View>
<View style={styles.statItem}>
<Text style={styles.statNumber}>
{parcels.filter(p => p.status === '已通知').length}
</Text>
<Text style={styles.statLabel}>已通知</Text>
</View>
<View style={styles.statItem}>
<Text style={styles.statNumber}>
{parcels.filter(p => p.status === '已取件').length}
</Text>
<Text style={styles.statLabel}>已取件</Text>
</View>
</View>
{/* 包裹列表 */}
<View style={styles.parcelsCard}>
<Text style={styles.sectionTitle}>到站包裹</Text>
{filteredParcels.map(parcel => (
<View key={parcel.id} style={styles.parcelItem}>
<View style={styles.parcelHeader}>
<Text style={styles.trackingNumber}>{parcel.trackingNumber}</Text>
<View style={[
styles.statusBadge,
{ backgroundColor: getStatusColor(parcel.status) }
]}>
<Text style={styles.statusText}>{parcel.status}</Text>
</View>
</View>
<View style={styles.parcelDetails}>
<View style={styles.detailRow}>
<Text style={styles.detailLabel}>收件人:</Text>
<Text style={styles.detailValue}>{parcel.recipientName}</Text>
</View>
<View style={styles.detailRow}>
<Text style={styles.detailLabel}>电话:</Text>
<Text style={styles.detailValue}>{parcel.recipientPhone}</Text>
</View>
<View style={styles.detailRow}>
<Text style={styles.detailLabel}>到站时间:</Text>
<Text style={styles.detailValue}>{parcel.arrivalTime}</Text>
</View>
<View style={styles.detailRow}>
<Text style={styles.detailLabel}>取件码:</Text>
<Text style={styles.pickupCode}>{parcel.pickupCode}</Text>
</View>
</View>
<View style={styles.parcelActions}>
{parcel.status === '待取件' && (
<>
<TouchableOpacity
style={styles.editButton}
onPress={() => handleEditRecipient(parcel.id)}
>
<Text style={styles.editButtonText}>编辑信息</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.notifyButton}
onPress={() => handleNotifyRecipient(parcel.id)}
>
<Text style={styles.notifyButtonText}>发送通知</Text>
</TouchableOpacity>
</>
)}
{parcel.status === '已通知' && (
<TouchableOpacity
style={styles.pickupButton}
onPress={() => handleMarkPickedUp(parcel.id)}
>
<Text style={styles.pickupButtonText}>标记取件</Text>
</TouchableOpacity>
)}
</View>
</View>
))}
</View>
{/* 操作说明 */}
<View style={styles.infoCard}>
<Text style={styles.sectionTitle}>操作说明</Text>
<Text style={styles.infoText}>• 扫描或手动添加到站包裹</Text>
<Text style={styles.infoText}>• 系统自动生成唯一取件码</Text>
<Text style={styles.infoText}>• 一键通知收件人取件信息</Text>
<Text style={styles.infoText}>• 实时更新包裹状态</Text>
</View>
</ScrollView>
{/* 底部导航 */}
<View style={styles.bottomNav}>
<TouchableOpacity style={styles.navItem}>
<Text style={styles.navIcon}>📦</Text>
<Text style={styles.navText}>包裹</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.navItem}>
<Text style={styles.navIcon}>🔔</Text>
<Text style={styles.navText}>通知</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.navItem}>
<Text style={styles.navIcon}>📊</Text>
<Text style={styles.navText}>统计</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.navItem, styles.activeNavItem]}>
<Text style={styles.navIcon}>👤</Text>
<Text style={styles.navText}>我的</Text>
</TouchableOpacity>
</View>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fefce8',
},
header: {
flexDirection: 'column',
padding: 16,
backgroundColor: '#ffffff',
borderBottomWidth: 1,
borderBottomColor: '#fef08a',
},
title: {
fontSize: 20,
fontWeight: 'bold',
color: '#854d0e',
marginBottom: 4,
},
subtitle: {
fontSize: 14,
color: '#ca8a04',
},
content: {
flex: 1,
marginTop: 12,
},
searchCard: {
backgroundColor: '#ffffff',
marginHorizontal: 16,
marginBottom: 12,
borderRadius: 12,
padding: 16,
elevation: 2,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
},
searchContainer: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#fefce8',
borderRadius: 8,
paddingHorizontal: 12,
marginBottom: 12,
},
searchIcon: {
fontSize: 16,
color: '#a16207',
marginRight: 8,
},
searchInput: {
flex: 1,
paddingVertical: 12,
fontSize: 14,
color: '#854d0e',
},
addButton: {
backgroundColor: '#ca8a04',
paddingVertical: 14,
borderRadius: 8,
alignItems: 'center',
},
addButtonText: {
color: '#ffffff',
fontSize: 16,
fontWeight: '600',
},
statsCard: {
backgroundColor: '#ffffff',
marginHorizontal: 16,
marginBottom: 12,
borderRadius: 12,
padding: 16,
elevation: 2,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
flexDirection: 'row',
justifyContent: 'space-around',
},
statItem: {
alignItems: 'center',
},
statNumber: {
fontSize: 18,
fontWeight: 'bold',
color: '#ca8a04',
},
statLabel: {
fontSize: 12,
color: '#64748b',
marginTop: 4,
},
parcelsCard: {
backgroundColor: '#ffffff',
marginHorizontal: 16,
marginBottom: 12,
borderRadius: 12,
padding: 16,
elevation: 2,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
},
sectionTitle: {
fontSize: 16,
fontWeight: '600',
color: '#854d0e',
marginBottom: 12,
},
parcelItem: {
padding: 12,
borderBottomWidth: 1,
borderBottomColor: '#fef08a',
},
parcelHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 8,
},
trackingNumber: {
fontSize: 16,
fontWeight: '600',
color: '#854d0e',
},
statusBadge: {
paddingHorizontal: 8,
paddingVertical: 4,
borderRadius: 12,
},
statusText: {
fontSize: 12,
color: '#ffffff',
fontWeight: '500',
},
parcelDetails: {
marginBottom: 12,
},
detailRow: {
flexDirection: 'row',
marginBottom: 4,
},
detailLabel: {
fontSize: 12,
color: '#64748b',
width: 70,
},
detailValue: {
fontSize: 12,
color: '#854d0e',
flex: 1,
},
pickupCode: {
fontSize: 16,
fontWeight: 'bold',
color: '#ca8a04',
},
parcelActions: {
flexDirection: 'row',
justifyContent: 'flex-end',
flexWrap: 'wrap',
},
editButton: {
backgroundColor: '#fde047',
paddingHorizontal: 12,
paddingVertical: 6,
borderRadius: 16,
marginRight: 8,
marginBottom: 4,
},
editButtonText: {
color: '#854d0e',
fontSize: 12,
},
notifyButton: {
backgroundColor: '#3b82f6',
paddingHorizontal: 12,
paddingVertical: 6,
borderRadius: 16,
marginRight: 8,
marginBottom: 4,
},
notifyButtonText: {
color: '#ffffff',
fontSize: 12,
},
pickupButton: {
backgroundColor: '#10b981',
paddingHorizontal: 12,
paddingVertical: 6,
borderRadius: 16,
marginBottom: 4,
},
pickupButtonText: {
color: '#ffffff',
fontSize: 12,
},
infoCard: {
backgroundColor: '#ffffff',
marginHorizontal: 16,
marginBottom: 80,
borderRadius: 12,
padding: 16,
elevation: 2,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
},
infoText: {
fontSize: 14,
color: '#64748b',
lineHeight: 20,
marginBottom: 4,
},
bottomNav: {
flexDirection: 'row',
justifyContent: 'space-around',
backgroundColor: '#ffffff',
borderTopWidth: 1,
borderTopColor: '#fef08a',
paddingVertical: 12,
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
},
navItem: {
alignItems: 'center',
flex: 1,
},
activeNavItem: {
paddingTop: 4,
borderTopWidth: 2,
borderTopColor: '#ca8a04',
},
navIcon: {
fontSize: 20,
color: '#94a3b8',
marginBottom: 4,
},
activeNavIcon: {
color: '#ca8a04',
},
navText: {
fontSize: 12,
color: '#94a3b8',
},
activeNavText: {
color: '#ca8a04',
fontWeight: '500',
},
});
export default ParcelStationApp;

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

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

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

本文介绍了基于React Native开发的驿站包裹管理应用如何适配鸿蒙系统。应用采用React函数式组件+TypeScript技术栈,通过强类型数据模型确保跨端一致性,使用useState轻量级状态管理实现多端状态同步。UI层深度复刻鸿蒙设计规范,采用Flex布局适配不同设备尺寸,并实现鸿蒙风格的视觉样式和交互组件。核心业务逻辑如取件码生成、状态流转和搜索筛选均采用纯JavaScript实现,天然具备跨端能力。这种技术方案为React Native应用对接鸿蒙系统提供了实践参考,有效降低了跨平台开发成本。
欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。
更多推荐



所有评论(0)