基于React Native鸿蒙跨平台实现可用性筛选和距离最优原则,支持人工干预和智能分配两种模式
本文介绍了一个基于React Native的跨平台外卖配送应用架构设计。系统采用函数式组件和TypeScript类型系统,定义了FoodOrder和Rider核心数据类型,管理订单全生命周期和骑手信息。通过useState实现订单创建、骑手分配和状态更新等核心业务流程,采用单向数据流模式确保数据一致性。文章详细阐述了状态管理机制、业务逻辑实现和性能优化策略,并针对鸿蒙平台提供了适配建议。该系统设计
欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。
整体架构
本项目采用React Native函数式组件架构,以FoodDeliveryApp为核心组件,实现了餐饮商家外卖配送的完整功能流程。架构设计遵循模块化原则,将数据结构、状态管理和业务逻辑清晰分离,便于维护和扩展。
核心技术栈
- React Native:跨平台移动应用开发框架,支持iOS、Android和鸿蒙系统
- TypeScript:提供类型安全,增强代码可维护性和开发体验
- Hooks API:使用useState进行状态管理,简化组件逻辑
- Flexbox:实现响应式布局,适配不同屏幕尺寸
- Base64图标:内置图标资源,减少网络请求,提升加载速度
- Dimensions API:获取屏幕尺寸,实现精细化布局控制
外卖订单类型(FoodOrder)
type FoodOrder = {
id: string;
customerName: string;
customerPhone: string;
deliveryAddress: string;
items: string[];
totalAmount: number;
orderTime: string;
estimatedDelivery: string;
status: '待接单' | '已接单' | '配送中' | '已完成';
riderId: string;
};
该类型设计全面,包含了外卖订单的核心信息:
id:唯一标识符,确保数据唯一性customerName:客户姓名,便于联系customerPhone:客户电话,用于订单沟通deliveryAddress:配送地址,确保准确送达items:菜品列表,记录订单内容totalAmount:订单金额,用于结算orderTime:下单时间,记录订单生命周期estimatedDelivery:预计送达时间,提升用户体验status:订单状态,使用联合类型确保类型安全,清晰定义业务流程riderId:骑手ID,建立订单与骑手的关联
骑手信息类型(Rider)
type Rider = {
id: string;
name: string;
phone: string;
vehicleType: string;
licensePlate: string;
distance: number;
rating: number;
isAvailable: boolean;
};
骑手信息类型设计合理,包含了配送服务的关键信息:
id:骑手唯一标识name:骑手姓名phone:联系电话,便于沟通vehicleType:交通工具类型,影响配送速度licensePlate:车牌号,用于车辆管理distance:距离信息,用于智能分配rating:评分信息,反映服务质量isAvailable:可用状态,确保分配有效的骑手
核心状态
应用使用useState钩子管理三个核心状态:
orders:外卖订单列表,支持动态更新riders:骑手信息列表,使用常量状态newOrder:新订单表单数据,支持用户输入
状态更新机制
- 订单创建:通过handleCreateOrder函数,验证表单数据后添加新订单
- 骑手分配:通过handleAssignRider函数,根据距离和可用性分配骑手
- 状态更新:通过setOrders函数,更新订单状态和骑手信息
- 表单重置:在创建订单后,自动重置表单状态
状态管理
- 不可变数据模式:使用扩展运算符(
...)创建新状态,避免直接修改原状态 - 数据验证:在创建订单前进行数据完整性检查
- 状态清理:在操作完成后,清理临时状态,保持界面整洁
- 批量更新:在状态更新时使用map方法批量处理,确保数据一致性
核心业务流程
- 订单管理:展示和管理外卖订单,包含完整的订单生命周期
- 订单创建:收集客户信息和订单内容,生成新订单
- 骑手分配:根据距离和可用性,智能分配骑手
- 状态跟踪:实时更新订单状态,确保业务流程透明
- 骑手联系:提供与骑手的沟通渠道,方便订单协调
核心业务逻辑
const handleAssignRider = (orderId: string) => {
const order = orders.find(o => o.id === orderId);
if (order) {
const availableRiders = riders.filter(r => r.isAvailable);
if (availableRiders.length > 0) {
Alert.alert(
'分配骑手',
`为订单${order.id}找到了${availableRiders.length}位附近骑手`,
[
{
text: '查看详情',
onPress: () => Alert.alert(
'骑手信息',
availableRiders.map(rider =>
`${rider.name} - ${rider.vehicleType} - 距离${rider.distance}km - 评分${rider.rating}`
).join('\n\n')
)
},
{
text: '分配最近骑手',
onPress: () => {
const nearestRider = availableRiders.reduce((prev, current) =>
prev.distance < current.distance ? prev : current
);
setOrders(orders.map(o =>
o.id === orderId
? { ...o, status: '已接单', riderId: nearestRider.id }
: o
));
Alert.alert('成功', `已分配给${nearestRider.name}`);
}
},
{ text: '取消', style: 'cancel' }
]
);
} else {
Alert.alert('提示', '暂时没有可用骑手,请稍后再试');
}
}
};
数据流设计
- 单向数据流:状态 → 视图 → 用户操作 → 状态更新
- 数据验证:在创建订单前进行数据完整性检查
- 业务逻辑封装:将复杂计算逻辑封装在专用函数中,提高代码可读性
- 用户交互反馈:使用Alert组件提供操作确认和信息提示,提升用户体验
组件
- 核心组件:SafeAreaView、View、Text、ScrollView、TouchableOpacity、TextInput等在鸿蒙系统上有对应实现
- API兼容性:Dimensions API在鸿蒙系统中可正常使用,确保布局适配
- Alert组件:鸿蒙系统支持Alert组件的基本功能,但样式可能有所差异
资源
- Base64图标:在鸿蒙系统中同样支持,可减少网络请求,提升性能
- 内存管理:鸿蒙系统对内存使用更为严格,需注意资源释放
- 计算性能:对于骑手分配等操作,鸿蒙系统的处理性能与其他平台相当
性能
-
渲染性能:
- 避免不必要的重渲染,合理使用React.memo
- 对于长订单列表,建议使用FlatList替代ScrollView
- 优化组件结构,减少嵌套层级
-
数据处理:
- 缓存计算结果,避免重复计算
- 优化骑手分配算法,特别是最近骑手的计算
- 考虑使用useMemo缓存计算结果,提高性能
-
内存管理:
- 及时释放不再使用的资源
- 避免内存泄漏,特别是在处理多个订单时
- 合理使用缓存策略,平衡性能和内存占用
平台适配建议
- 条件渲染:使用Platform API检测平台,针对不同平台使用不同实现
- 样式适配:考虑鸿蒙系统的设计规范,调整UI样式以符合平台特性
- 权限处理:鸿蒙系统的权限管理与Android有所不同,需单独处理
- 鸿蒙特性:充分利用鸿蒙系统的分布式能力,实现多设备协同
- 定位服务:针对鸿蒙系统的定位服务,调整骑手位置获取方式
1. 类型定义
- 使用枚举类型:将订单状态、交通工具类型等使用枚举类型替代字符串,提高代码可读性和可维护性
- 类型扩展:考虑使用接口继承,增强类型系统的表达能力
- 类型守卫:添加更多类型守卫,确保运行时类型安全
- 国际化支持:考虑添加国际化支持,特别是订单状态等术语
2. 状态管理
- 状态分离:对于复杂状态,考虑使用useReducer或状态管理库(如Redux、Zustand)
- 状态持久化:实现状态持久化,使用AsyncStorage存储订单数据
- 计算属性:使用useMemo缓存计算结果,避免重复计算
- 批量操作:支持批量管理订单,提高商家效率
3. 组件化
- 组件拆分:将大型组件拆分为更小的可复用组件,如OrderCard、RiderCard、OrderForm等
- 自定义Hook:提取重复的状态逻辑到自定义Hook,如useOrderManager、useRiderAssigner等
- 高阶组件:使用高阶组件处理横切关注点,如错误边界、加载状态等
- 表单组件:封装表单输入组件,提高代码复用性
4. 业务逻辑
- 服务层分离:将业务逻辑分离到服务层,提高代码可测试性
- 错误处理:增强错误处理机制,提供更友好的错误提示
- 实时更新:考虑添加实时订单状态更新,提升用户体验
- 智能分配:实现更智能的骑手分配算法,考虑更多因素如历史配送时间、订单优先级等
5. 性能
- 列表优化:使用FlatList的性能优化特性,如getItemLayout、initialNumToRender等
- 网络优化:实现请求防抖和节流,减少网络请求频率
- Bundle优化:使用代码分割和Tree Shaking,减少应用体积
- 启动优化:优化应用启动速度,减少初始加载时间
- 完整的外卖配送流程:实现了从订单创建到骑手分配的完整外卖配送流程
- 智能骑手分配:根据距离和可用性,自动分配最合适的骑手
- 实时订单管理:实时更新订单状态,确保业务流程透明
- 类型安全:充分利用TypeScript的类型系统,提高代码质量和开发效率
- 响应式设计:使用Flexbox和Dimensions API,实现适配不同屏幕尺寸的布局
- 内置图标资源:使用Base64编码图标,减少网络依赖,提升加载速度
- 用户友好的交互:提供清晰的操作反馈和信息提示,提升用户体验
- 模块化架构:代码结构模块化,便于维护和扩展
- 数据验证:在创建订单前进行数据完整性检查,确保数据质量
- 智能算法:实现了基于距离的骑手分配算法,提高配送效率
本项目展示了如何使用React Native和TypeScript构建一个功能完整的餐饮商家外卖配送应用。通过合理的架构设计、类型定义和状态管理,实现了跨平台的一致性体验。
随着React Native和鸿蒙系统的不断发展,跨端开发将会变得更加成熟和高效。未来可以考虑:
- 使用React Native 0.70+版本:利用新的架构特性,如Fabric渲染器和Turbo Modules,提升应用性能
- 探索鸿蒙原生能力:充分利用鸿蒙系统的分布式能力,实现多设备协同和更丰富的功能
- 引入现代化状态管理:使用Redux Toolkit或Zustand等现代状态管理库,简化状态管理
- 实现PWA支持:扩展应用的使用场景,支持Web平台
- 集成AI能力:引入AI技术,如智能路线规划、订单预测等,提升应用智能化水平
- 实时通信:集成WebSocket,实现实时订单状态更新和骑手位置追踪
通过持续的技术迭代和优化,可以构建更加稳定、高效、智能的外卖配送应用,满足餐饮商家的业务需求,提升用户体验。
外卖配送管理系统作为本地生活服务的核心数字化工具,需兼顾订单管理的完整性、骑手匹配的智能化、操作流程的便捷性三大核心诉求。本文将深度拆解基于 React Native 开发的餐饮外卖配送管理应用,剖析其订单建模、骑手匹配、状态管理的核心技术实现,并提供完整的鸿蒙(HarmonyOS)ArkTS 跨端适配方案,为外卖配送类应用的跨端开发提供可落地的技术参考。
1.外卖配送场景
外卖配送管理系统区别于普通订单系统的核心在于订单全生命周期管理、骑手资源匹配、配送状态追踪。代码通过 TypeScript 类型系统构建了精准贴合外卖配送场景的领域模型,充分体现了外卖配送的行业特性:
// 外卖订单类型
type FoodOrder = {
id: string;
customerName: string;
customerPhone: string;
deliveryAddress: string;
items: string[];
totalAmount: number;
orderTime: string;
estimatedDelivery: string;
status: '待接单' | '已接单' | '配送中' | '已完成';
riderId: string;
};
// 骑手信息类型
type Rider = {
id: string;
name: string;
phone: string;
vehicleType: string;
licensePlate: string;
distance: number;
rating: number;
isAvailable: boolean;
};
模型设计的外卖场景适配性分析:
- 订单维度完整性:
FoodOrder模型覆盖客户信息、配送地址、菜品清单、金额、时间、状态、骑手关联等外卖订单全维度信息,满足配送管理的核心数据需求; - 状态管控精细化:采用"待接单/已接单/配送中/已完成"四态管理,贴合外卖订单的全生命周期流程,每个状态对应明确的业务操作节点;
- 骑手资源结构化:
Rider模型包含基础信息、交通工具、位置距离、服务评分、在线状态等核心维度,为智能匹配提供数据支撑; - 空间维度适配:
distance字段量化骑手与商家的距离,是智能匹配算法的核心依据,符合外卖配送的位置服务特性; - 服务质量量化:
rating评分字段体现骑手服务质量,支持按评分优先的匹配策略,提升用户体验; - 可用性状态化:
isAvailable字段标记骑手在线状态,保证订单分配的有效性,避免无效匹配; - 类型安全保障:订单状态采用字面量类型约束,避免非法状态值输入,保证订单数据的严谨性;
- 关联关系明确:通过
riderId建立订单与骑手的关联,支持后续的骑手联系、配送追踪等操作。
2. 外卖配送的全流程
外卖配送管理的核心痛点是订单创建的完整性校验、骑手匹配的智能化、状态流转的准确性,代码通过轻量化但专业的逻辑实现了外卖配送管理的核心业务流程:
(1)订单创建
外卖订单创建是配送流程的起点,必须确保核心信息的完整性和格式的正确性:
const handleCreateOrder = () => {
if (!newOrder.customerName || !newOrder.customerPhone || !newOrder.deliveryAddress) {
Alert.alert('提示', '请填写完整的订单信息');
return;
}
const order: FoodOrder = {
id: (orders.length + 1).toString(),
customerName: newOrder.customerName,
customerPhone: newOrder.customerPhone,
deliveryAddress: newOrder.deliveryAddress,
items: newOrder.items.split(',').map(item => item.trim()),
totalAmount: parseFloat(newOrder.totalAmount) || 0,
orderTime: new Date().toLocaleString('zh-CN'),
estimatedDelivery: '待分配',
status: '待接单',
riderId: ''
};
setOrders([...orders, order]);
setNewOrder({
customerName: '',
customerPhone: '',
deliveryAddress: '',
items: '',
totalAmount: ''
});
Alert.alert('成功', '外卖订单创建成功!');
};
订单创建逻辑的场景适配性:
- 核心字段校验:优先校验姓名、电话、地址等必填字段,保证订单的基础有效性,符合外卖配送的核心信息要求;
- 数据格式处理:菜品信息按逗号分割并去空格,金额字段转换为数值类型,保证数据格式的规范性;
- 默认值设置:金额字段提供0值兜底,避免空值导致的计算异常,保证订单数据的完整性;
- 时间戳自动生成:订单创建时间自动获取当前时间,符合外卖订单的时间管理规范;
- 初始状态设定:新订单默认状态为"待接单",骑手ID为空,符合外卖订单的初始状态逻辑;
- 状态更新机制:采用不可变数据模式更新订单列表,避免直接修改原数组,符合React状态管理最佳实践;
- 表单重置:订单创建成功后清空表单,提升操作效率,符合连续创建订单的业务场景;
- 操作反馈:提供明确的成功提示,保证商家操作人员的感知,符合交互设计的反馈原则;
- 异常防护:前置校验避免无效订单创建,减少后续配送环节的异常情况。
(2)骑手智能匹配
骑手匹配是外卖配送的核心环节,需基于骑手可用性和距离实现最优匹配:
const handleAssignRider = (orderId: string) => {
const order = orders.find(o => o.id === orderId);
if (order) {
const availableRiders = riders.filter(r => r.isAvailable);
if (availableRiders.length > 0) {
Alert.alert(
'分配骑手',
`为订单${order.id}找到了${availableRiders.length}位附近骑手`,
[
{
text: '查看详情',
onPress: () => Alert.alert(
'骑手信息',
availableRiders.map(rider =>
`${rider.name} - ${rider.vehicleType} - 距离${rider.distance}km - 评分${rider.rating}`
).join('\n\n')
)
},
{
text: '分配最近骑手',
onPress: () => {
const nearestRider = availableRiders.reduce((prev, current) =>
prev.distance < current.distance ? prev : current
);
setOrders(orders.map(o =>
o.id === orderId
? { ...o, status: '已接单', riderId: nearestRider.id }
: o
));
Alert.alert('成功', `已分配给${nearestRider.name}`);
}
},
{ text: '取消', style: 'cancel' }
]
);
} else {
Alert.alert('提示', '暂时没有可用骑手,请稍后再试');
}
}
};
骑手匹配逻辑的智能化设计:
- 可用性筛选:优先过滤出在线骑手,保证匹配的有效性,避免分配给离线骑手导致的配送延误;
- 数量统计反馈:告知商家可用骑手数量,提供决策参考,符合外卖配送的透明化管理需求;
- 详情展示:提供骑手姓名、车型、距离、评分等核心信息,支持商家人工决策,兼顾智能化与人工干预;
- 最优匹配算法:通过
reduce方法找到距离最近的骑手,实现基础的智能匹配,符合外卖配送的效率优先原则; - 状态联动更新:骑手分配成功后自动更新订单状态为"已接单",并关联骑手ID,保证订单状态的一致性;
- 不可变更新:采用数组映射方式更新订单状态,避免直接修改,符合React状态管理规范;
- 异常处理:无可用骑手时给出明确提示,引导商家稍后重试,提升用户体验;
- 操作选项:提供查看详情、分配最优骑手、取消三个选项,满足不同的业务操作需求;
- 结果反馈:分配成功后提示具体骑手姓名,保证商家的操作感知,便于后续沟通。
(3)骑手联系
骑手分配后需支持快速联系,满足配送过程中的沟通需求:
const handleContactRider = (riderId: string) => {
const rider = riders.find(r => r.id === riderId);
if (rider) {
Alert.alert('联系骑手', `拨打 ${rider.phone}`, [
{ text: '取消', style: 'cancel' },
{ text: '拨打', onPress: () => Alert.alert('提示', '正在拨打电话...') }
]);
}
};
骑手联系逻辑的实用性设计:
- 数据关联:通过骑手ID精准匹配骑手信息,保证联系对象的准确性;
- 操作引导:明确提示拨打的电话号码,避免拨号错误,符合外卖配送的沟通需求;
- 操作选项:提供取消和拨打两个选项,符合移动端拨号的交互习惯;
- 反馈提示:拨打操作给出"正在拨打电话"提示,模拟真实拨号流程,提升体验;
- 异常防护:先校验骑手信息是否存在,避免空指针异常,保证系统稳定性;
- 权限适配:预留拨号接口,可直接对接系统拨号功能,符合外卖配送的实际业务需求。
3. 外卖场景
外卖配送管理系统的视觉设计围绕效率性、辨识度、易用性三个核心维度展开,贴合餐饮商家操作人员的使用习惯和业务特性:
(1)外卖场景
- 行业化色调体系:采用暖色系(
#f97316/#9a3412)为主色调,契合餐饮外卖的行业属性,区别于其他类型应用的视觉风格; - 状态视觉差异化:不同订单状态对应不同色系(待接单-黄色、已接单-蓝色、配送中-紫色、已完成-绿色),符合外卖配送的视觉识别习惯,便于快速识别订单状态;
- 表单设计场景化:订单创建表单按姓名、电话、地址、菜品、金额的逻辑顺序排列,符合商家录入订单的思维习惯;
- 信息层级化展示:订单列表中核心信息(客户姓名、状态)突出展示,详情信息(地址、菜品)次级展示,提升信息读取效率;
- 操作按钮场景化:不同状态订单展示不同操作按钮(待接单-分配骑手、已接单-联系骑手),符合外卖配送的业务流程;
- 骑手信息可视化:骑手列表展示姓名、车型、距离、评分、在线状态等核心信息,便于商家快速评估骑手资源;
- 底部导航场景化:按店铺、骑手、订单、我的分类,贴合外卖配送管理的核心业务流程;
- 安全区域适配:使用 SafeAreaView 适配异形屏,保证操作界面的完整性;
- 卡片层级化设计:所有功能模块采用卡片式设计+轻微阴影,提升界面层次感,符合移动端管理系统的设计趋势。
(2)外卖场景
- 状态标签化展示:订单状态以彩色徽章形式展示,视觉突出,便于快速识别订单进度;
- 表单输入优化:地址输入框支持多行输入,电话和金额输入框适配对应键盘类型,提升录入效率;
- 操作按钮动态展示:根据订单状态动态展示可用操作按钮,避免无效操作,符合外卖配送的流程逻辑;
- 骑手状态可视化:通过圆形指示器直观展示骑手在线/离线状态,提升识别效率;
- 底部导航固定展示:核心管理功能入口固定展示,便于操作过程中的功能快速切换;
- 滚动体验流畅化:订单和骑手列表支持滚动,适配多订单/多骑手管理的展示需求;
- 操作反馈明确化:所有操作均提供明确的结果提示,保证商家操作的可追溯性;
- 信息录入便捷化:菜品信息支持逗号分隔录入,符合商家快速录入的习惯,提升订单创建效率。
将 React Native 外卖配送管理系统迁移至鸿蒙平台,核心是基于 ArkTS + ArkUI 实现类型系统、状态管理、业务逻辑、视觉交互的对等还原,同时适配鸿蒙的组件特性和布局范式,保证外卖配送管理体验的一致性和专业性。
1. 架构
鸿蒙端适配遵循类型复用、逻辑对等、体验统一的原则,外卖配送的核心业务逻辑和视觉规范100%复用,仅需适配平台特有API和组件语法,确保外卖配送应用的跨端体验一致性:
@Entry
@Component
struct FoodDeliveryApp {
// 类型定义:对等实现 TypeScript 类型 → 接口定义
interface FoodOrder {
id: string;
customerName: string;
customerPhone: string;
deliveryAddress: string;
items: string[];
totalAmount: number;
orderTime: string;
estimatedDelivery: string;
status: '待接单' | '已接单' | '配送中' | '已完成';
riderId: string;
}
interface Rider {
id: string;
name: string;
phone: string;
vehicleType: string;
licensePlate: string;
distance: number;
rating: number;
isAvailable: boolean;
}
// 状态管理:对等实现 useState → @State
@State orders: FoodOrder[] = [/* 初始订单数据 */];
@State riders: Rider[] = [/* 初始骑手数据 */];
@State newOrder: {
customerName: string;
customerPhone: string;
deliveryAddress: string;
items: string;
totalAmount: string;
} = {
customerName: '',
customerPhone: '',
deliveryAddress: '',
items: '',
totalAmount: ''
};
// 业务逻辑:完全复用 RN 端的外卖配送核心逻辑
getStatusColor(status: string): string {/* 状态颜色映射 */}
handleCreateOrder(): void {/* 创建订单 */}
handleAssignRider(orderId: string): void {/* 分配骑手 */}
handleContactRider(riderId: string): void {/* 联系骑手 */}
// 页面构建:镜像 RN 端布局结构,适配鸿蒙组件特性
build() {
Column() {
// 头部区域
// 订单创建表单
// 订单列表
// 骑手列表
// 配送说明
// 底部导航
}
}
}
| React Native 特性 | 鸿蒙 ArkUI 对应实现 | 外卖场景适配关键说明 |
|---|---|---|
| TypeScript 类型定义 | TypeScript 接口定义 | 订单、骑手等类型完全复用,保证外卖数据结构一致性 |
useState |
@State 装饰器 |
订单列表、骑手列表、表单状态等管理逻辑完全复用,保持配送流程一致性 |
| 订单创建算法 | 函数逻辑完全复用 | 字段校验、数据格式化、状态更新规则一致,保证订单创建的完整性 |
TextInput |
TextInput 组件 |
输入框属性适配,电话/金额输入类型对等实现,保持录入体验一致 |
TouchableOpacity |
Column + onClick |
所有可点击区域通过 onClick 事件实现,保持操作交互一致性 |
Alert.alert |
AlertDialog.show |
订单创建、骑手分配等弹窗逻辑对等,符合外卖操作习惯 |
StyleSheet |
链式样式 | 暖色系主色调、订单卡片样式等视觉规范100%复用 |
Array.map |
ForEach 组件 |
订单/骑手列表渲染逻辑一致,适配多订单/多骑手展示需求 |
ScrollView |
Scroll 组件 |
滚动容器语法差异,功能一致,适配长列表展示 |
| 状态颜色映射 | 函数逻辑完全复用 | 状态-颜色映射规则一致,保证外卖订单状态的视觉认知一致性 |
| 底部导航 | Position.Fixed |
导航栏定位语法差异,效果一致,保证配送管理操作的便捷性 |
3. 鸿蒙代码
// 鸿蒙 ArkTS 完整实现 - 外卖配送管理系统
@Entry
@Component
struct FoodDeliveryApp {
// 类型定义
interface FoodOrder {
id: string;
customerName: string;
customerPhone: string;
deliveryAddress: string;
items: string[];
totalAmount: number;
orderTime: string;
estimatedDelivery: string;
status: '待接单' | '已接单' | '配送中' | '已完成';
riderId: string;
}
interface Rider {
id: string;
name: string;
phone: string;
vehicleType: string;
licensePlate: string;
distance: number;
rating: number;
isAvailable: boolean;
}
// 状态管理
@State orders: FoodOrder[] = [
{
id: '1',
customerName: '张先生',
customerPhone: '138****1234',
deliveryAddress: '北京市朝阳区某某小区12号楼',
items: ['宫保鸡丁', '麻婆豆腐', '米饭'],
totalAmount: 68,
orderTime: '2023-12-01 18:30',
estimatedDelivery: '2023-12-01 19:00',
status: '待接单',
riderId: ''
},
{
id: '2',
customerName: '李女士',
customerPhone: '139****5678',
deliveryAddress: '上海市浦东新区某写字楼',
items: ['红烧肉', '青菜', '汤'],
totalAmount: 56,
orderTime: '2023-12-01 19:15',
estimatedDelivery: '2023-12-01 19:45',
status: '已接单',
riderId: 'r1'
}
];
@State riders: Rider[] = [
{
id: 'r1',
name: '王骑手',
phone: '138****1234',
vehicleType: '电动车',
licensePlate: '京A12345',
distance: 1.2,
rating: 4.8,
isAvailable: true
},
{
id: 'r2',
name: '刘骑手',
phone: '139****5678',
vehicleType: '摩托车',
licensePlate: '沪B67890',
distance: 2.5,
rating: 4.9,
isAvailable: false
}
];
@State newOrder: {
customerName: string;
customerPhone: string;
deliveryAddress: string;
items: string;
totalAmount: string;
} = {
customerName: '',
customerPhone: '',
deliveryAddress: '',
items: '',
totalAmount: ''
};
// 获取状态对应的颜色
getStatusColor(status: string): string {
switch (status) {
case '待接单': return '#f59e0b';
case '已接单': return '#3b82f6';
case '配送中': return '#8b5cf6';
case '已完成': return '#10b981';
default: return '#6b7280';
}
}
// 创建订单
handleCreateOrder(): void {
if (!this.newOrder.customerName || !this.newOrder.customerPhone || !this.newOrder.deliveryAddress) {
AlertDialog.show({
title: '提示',
message: '请填写完整的订单信息',
confirm: { value: '确定' }
});
return;
}
const order: FoodOrder = {
id: (this.orders.length + 1).toString(),
customerName: this.newOrder.customerName,
customerPhone: this.newOrder.customerPhone,
deliveryAddress: this.newOrder.deliveryAddress,
items: this.newOrder.items.split(',').map(item => item.trim()),
totalAmount: parseFloat(this.newOrder.totalAmount) || 0,
orderTime: new Date().toLocaleString('zh-CN'),
estimatedDelivery: '待分配',
status: '待接单',
riderId: ''
};
this.orders = [...this.orders, order];
this.newOrder = {
customerName: '',
customerPhone: '',
deliveryAddress: '',
items: '',
totalAmount: ''
};
AlertDialog.show({
title: '成功',
message: '外卖订单创建成功!',
confirm: { value: '确定' }
});
}
// 分配骑手
handleAssignRider(orderId: string): void {
const order = this.orders.find(o => o.id === orderId);
if (order) {
const availableRiders = this.riders.filter(r => r.isAvailable);
if (availableRiders.length > 0) {
AlertDialog.show({
title: '分配骑手',
message: `为订单${order.id}找到了${availableRiders.length}位附近骑手`,
buttons: [
{
value: '查看详情',
action: () => {
AlertDialog.show({
title: '骑手信息',
message: availableRiders.map(rider =>
`${rider.name} - ${rider.vehicleType} - 距离${rider.distance}km - 评分${rider.rating}`
).join('\n\n'),
confirm: { value: '确定' }
});
}
},
{
value: '分配最近骑手',
action: () => {
const nearestRider = availableRiders.reduce((prev, current) =>
prev.distance < current.distance ? prev : current
);
this.orders = this.orders.map(o =>
o.id === orderId
? { ...o, status: '已接单', riderId: nearestRider.id }
: o
);
AlertDialog.show({
title: '成功',
message: `已分配给${nearestRider.name}`,
confirm: { value: '确定' }
});
}
},
{
value: '取消',
action: () => {}
}
]
});
} else {
AlertDialog.show({
title: '提示',
message: '暂时没有可用骑手,请稍后再试',
confirm: { value: '确定' }
});
}
}
}
// 联系骑手
handleContactRider(riderId: string): void {
const rider = this.riders.find(r => r.id === riderId);
if (rider) {
AlertDialog.show({
title: '联系骑手',
message: `拨打 ${rider.phone}`,
buttons: [
{
value: '取消',
action: () => {}
},
{
value: '拨打',
action: () => {
AlertDialog.show({
title: '提示',
message: '正在拨打电话...',
confirm: { value: '确定' }
});
}
}
]
});
}
}
build() {
Column()
.flex(1)
.backgroundColor('#fffbeb')
.safeArea(true) {
// 头部区域
Column()
.padding(16)
.backgroundColor('#ffffff')
.borderBottom({ width: 1, color: '#fed7aa' }) {
Text('外卖配送管理')
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor('#9a3412')
.marginBottom(4);
Text('快速接单,准时送达')
.fontSize(14)
.fontColor('#ea580c');
}
// 滚动内容区
Scroll()
.flex(1)
.marginTop(12) {
Column() {
// 创建新订单卡片
Column()
.backgroundColor('#ffffff')
.marginLeft(16)
.marginRight(16)
.marginBottom(12)
.borderRadius(12)
.padding(16)
.shadow({ color: '#000', offsetX: 0, offsetY: 2, opacity: 0.1, radius: 4 }) {
Text('创建外卖订单')
.fontSize(16)
.fontWeight(FontWeight.SemiBold)
.fontColor('#9a3412')
.marginBottom(12);
// 客户姓名输入框
TextInput({
placeholder: '客户姓名',
text: this.newOrder.customerName
})
.borderWidth(1)
.borderColor('#fed7aa')
.borderRadius(8)
.padding(12)
.fontSize(14)
.backgroundColor('#fff7ed')
.marginBottom(12)
.onChangeText((text) => {
this.newOrder.customerName = text;
});
// 客户电话输入框
TextInput({
placeholder: '客户电话',
text: this.newOrder.customerPhone
})
.borderWidth(1)
.borderColor('#fed7aa')
.borderRadius(8)
.padding(12)
.fontSize(14)
.backgroundColor('#fff7ed')
.marginBottom(12)
.type(InputType.Number)
.onChangeText((text) => {
this.newOrder.customerPhone = text;
});
// 配送地址输入框
TextInput({
placeholder: '配送地址',
text: this.newOrder.deliveryAddress
})
.borderWidth(1)
.borderColor('#fed7aa')
.borderRadius(8)
.padding(12)
.fontSize(14)
.backgroundColor('#fff7ed')
.marginBottom(12)
.minHeight(80)
.multiline(true)
.onChangeText((text) => {
this.newOrder.deliveryAddress = text;
});
// 菜品输入框
TextInput({
placeholder: '菜品(用逗号分隔)',
text: this.newOrder.items
})
.borderWidth(1)
.borderColor('#fed7aa')
.borderRadius(8)
.padding(12)
.fontSize(14)
.backgroundColor('#fff7ed')
.marginBottom(12)
.onChangeText((text) => {
this.newOrder.items = text;
});
// 订单金额输入框
TextInput({
placeholder: '订单金额(元)',
text: this.newOrder.totalAmount
})
.borderWidth(1)
.borderColor('#fed7aa')
.borderRadius(8)
.padding(12)
.fontSize(14)
.backgroundColor('#fff7ed')
.marginBottom(12)
.type(InputType.Number)
.onChangeText((text) => {
this.newOrder.totalAmount = text;
});
// 创建订单按钮
Button('创建订单')
.backgroundColor('#f97316')
.paddingVertical(14)
.borderRadius(8)
.fontSize(16)
.fontWeight(FontWeight.SemiBold)
.fontColor(Color.White)
.onClick(() => this.handleCreateOrder());
}
// 订单列表卡片
Column()
.backgroundColor('#ffffff')
.marginLeft(16)
.marginRight(16)
.marginBottom(12)
.borderRadius(12)
.padding(16)
.shadow({ color: '#000', offsetX: 0, offsetY: 2, opacity: 0.1, radius: 4 }) {
Text('外卖订单')
.fontSize(16)
.fontWeight(FontWeight.SemiBold)
.fontColor('#9a3412')
.marginBottom(12);
// 订单列表
ForEach(this.orders, (order: FoodOrder) => {
Column()
.padding(12)
.borderBottom({ width: 1, color: '#fed7aa' }) {
// 订单头部
Column()
.flexDirection(FlexDirection.Row)
.justifyContent(FlexAlign.SpaceBetween)
.alignItems(ItemAlign.Center)
.marginBottom(8) {
Text(order.customerName)
.fontSize(16)
.fontWeight(FontWeight.SemiBold)
.fontColor('#9a3412');
Column()
.paddingLeft(8)
.paddingRight(8)
.paddingTop(4)
.paddingBottom(4)
.borderRadius(12)
.backgroundColor(this.getStatusColor(order.status)) {
Text(order.status)
.fontSize(12)
.fontColor(Color.White)
.fontWeight(FontWeight.Medium);
}
}
// 订单详情
Column()
.marginBottom(12) {
ForEach([
{ label: '电话:', value: order.customerPhone },
{ label: '地址:', value: order.deliveryAddress },
{ label: '菜品:', value: order.items.join('、') },
{ label: '金额:', value: `¥${order.totalAmount}` }
], (item) => {
Column()
.flexDirection(FlexDirection.Row)
.marginBottom(4) {
Text(item.label)
.fontSize(12)
.fontColor('#64748b')
.width(50);
Text(item.value)
.fontSize(12)
.fontColor('#9a3412')
.flexGrow(1);
}
});
}
// 订单操作按钮
Column()
.flexDirection(FlexDirection.Row)
.justifyContent(FlexAlign.FlexEnd) {
// 分配骑手按钮(仅待接单状态显示)
if (order.status === '待接单') {
Button('分配骑手')
.backgroundColor('#10b981')
.paddingLeft(12)
.paddingRight(12)
.paddingTop(6)
.paddingBottom(6)
.borderRadius(16)
.fontSize(12)
.fontColor(Color.White)
.marginRight(8)
.onClick(() => this.handleAssignRider(order.id));
}
// 联系骑手按钮(仅分配骑手后显示)
if (order.riderId) {
Button('联系骑手')
.backgroundColor('#fed7aa')
.paddingLeft(12)
.paddingRight(12)
.paddingTop(6)
.paddingBottom(6)
.borderRadius(16)
.fontSize(12)
.fontColor('#f97316')
.marginRight(8)
.onClick(() => this.handleContactRider(order.riderId));
}
// 查看详情按钮
Button('查看详情')
.backgroundColor('#fed7aa')
.paddingLeft(12)
.paddingRight(12)
.paddingTop(6)
.paddingBottom(6)
.borderRadius(16)
.fontSize(12)
.fontColor('#f97316')
.onClick(() => {
AlertDialog.show({
title: '详情',
message: `订单ID: ${order.id}`,
confirm: { value: '确定' }
});
});
}
}
})
}
// 可用骑手卡片
Column()
.backgroundColor('#ffffff')
.marginLeft(16)
.marginRight(16)
.marginBottom(12)
.borderRadius(12)
.padding(16)
.shadow({ color: '#000', offsetX: 0, offsetY: 2, opacity: 0.1, radius: 4 }) {
Text('附近骑手')
.fontSize(16)
.fontWeight(FontWeight.SemiBold)
.fontColor('#9a3412')
.marginBottom(12);
// 骑手列表
ForEach(this.riders, (rider: Rider) => {
Column()
.flexDirection(FlexDirection.Row)
.justifyContent(FlexAlign.SpaceBetween)
.alignItems(ItemAlign.Center)
.paddingVertical(12)
.borderBottom({ width: 1, color: '#fed7aa' }) {
// 骑手信息
Column()
.flexGrow(1) {
Text(rider.name)
.fontSize(16)
.fontWeight(FontWeight.SemiBold)
.fontColor('#9a3412')
.marginBottom(4);
Text(`${rider.vehicleType} • ${rider.licensePlate}`)
.fontSize(14)
.fontColor('#64748b')
.marginBottom(4);
Column()
.flexDirection(FlexDirection.Row)
.justifyContent(FlexAlign.SpaceBetween) {
Text(`⭐ ${rider.rating}`);
Text(`距离: ${rider.distance}km`)
.fontSize(12)
.fontColor('#64748b');
}
}
// 骑手状态
Column()
.alignItems(ItemAlign.Center)
.marginRight(12) {
Column()
.width(12)
.height(12)
.borderRadius(6)
.backgroundColor(rider.isAvailable ? '#10b981' : '#6b7280')
.marginBottom(4);
Text(rider.isAvailable ? '在线' : '离线')
.fontSize(12)
.fontColor(Color.White);
}
// 联系按钮
Button('联系')
.backgroundColor('#fed7aa')
.paddingLeft(12)
.paddingRight(12)
.paddingTop(6)
.paddingBottom(6)
.borderRadius(16)
.fontSize(12)
.fontColor('#f97316')
.onClick(() => this.handleContactRider(rider.id));
}
})
}
// 配送说明卡片
Column()
.backgroundColor('#ffffff')
.marginLeft(16)
.marginRight(16)
.marginBottom(80)
.borderRadius(12)
.padding(16)
.shadow({ color: '#000', offsetX: 0, offsetY: 2, opacity: 0.1, radius: 4 }) {
Text('配送说明')
.fontSize(16)
.fontWeight(FontWeight.SemiBold)
.fontColor('#9a3412')
.marginBottom(12);
Text('• 系统根据距离智能匹配最近骑手')
.fontSize(14)
.fontColor('#64748b')
.lineHeight(20)
.marginBottom(4);
Text('• 实时跟踪订单配送状态')
.fontSize(14)
.fontColor('#64748b')
.lineHeight(20)
.marginBottom(4);
Text('• 支持骑手与客户直接联系')
.fontSize(14)
.fontColor('#64748b')
.lineHeight(20)
.marginBottom(4);
Text('• 异常情况及时处理')
.fontSize(14)
.fontColor('#64748b')
.lineHeight(20);
}
}
}
// 底部导航
Column()
.flexDirection(FlexDirection.Row)
.justifyContent(FlexAlign.SpaceAround)
.backgroundColor('#ffffff')
.borderTop({ width: 1, color: '#fed7aa' })
.paddingVertical(12)
.position(Position.Fixed)
.bottom(0)
.width('100%') {
// 店铺
Column()
.alignItems(ItemAlign.Center)
.flexGrow(1) {
Text('🏪')
.fontSize(20)
.fontColor('#94a3b8')
.marginBottom(4);
Text('店铺')
.fontSize(12)
.fontColor('#94a3b8');
}
// 骑手
Column()
.alignItems(ItemAlign.Center)
.flexGrow(1) {
Text('🛵')
.fontSize(20)
.fontColor('#94a3b8')
.marginBottom(4);
Text('骑手')
.fontSize(12)
.fontColor('#94a3b8');
}
// 订单
Column()
.alignItems(ItemAlign.Center)
.flexGrow(1) {
Text('📋')
.fontSize(20)
.fontColor('#94a3b8')
.marginBottom(4);
Text('订单')
.fontSize(12)
.fontColor('#94a3b8');
}
// 我的(激活状态)
Column()
.alignItems(ItemAlign.Center)
.flexGrow(1)
.paddingTop(4)
.borderTop({ width: 2, color: '#f97316' }) {
Text('👤')
.fontSize(20)
.fontColor('#f97316')
.marginBottom(4);
Text('我的')
.fontSize(12)
.fontColor('#f97316')
.fontWeight(FontWeight.Medium);
}
}
}
}
1. 原则
- 外卖模型专业化复用:订单、骑手等类型定义在两端保持一致,包含客户信息、配送地址、菜品、金额、骑手距离/评分等核心字段,保证外卖配送的专业性;
- 视觉规范100%对齐:暖色系主色调、订单卡片样式、状态颜色映射等视觉属性完全复用,符合餐饮外卖的行业视觉认知;
- 业务逻辑统一:订单创建、骑手匹配、状态管理等核心逻辑保持一致,保证外卖配送流程的合规性和效率;
- 交互体验一致:表单录入、按钮操作、弹窗提示等交互逻辑保持一致,降低商家操作人员的学习成本;
- 行业特性兼容:外卖配送的距离匹配、状态流转、骑手联系等行业特性在两端完整实现,满足餐饮商家的核心需求;
- 平台特性适配:弹窗展示、输入框类型、点击事件等平台特有API做适配处理,保证外卖配送应用的可用性;
- 性能优化适配:鸿蒙端利用 ForEach 组件的复用机制优化列表渲染性能,RN 端利用数组映射实现高效渲染,均满足多订单/多骑手展示的性能要求。
2. 外卖配送系统
- 地图集成:对接高德/百度地图SDK,实现配送地址的可视化选择和骑手位置的实时追踪;
- 支付集成:对接微信/支付宝支付接口,支持在线支付和订单金额的自动计算;
- 消息推送:集成推送服务,实现订单状态变更、骑手接单、配送完成等关键节点的消息提醒;
- 批量操作:增加批量打印订单、批量分配骑手等功能,提升高峰期订单处理效率;
- 数据分析:增加订单统计、配送效率分析、骑手绩效分析等功能,为商家运营决策提供数据支撑;
- 离线操作:支持离线状态下的基础订单管理操作,联网后自动同步数据;
- 语音交互:增加语音录入订单功能,提升商家订单创建效率;
- 电子小票:对接小票打印机,实现订单创建后自动打印配送小票;
- 多端数据同步:实现React Native和鸿蒙端的数据实时同步,保证订单和骑手信息的一致性;
- 权限精细化管理:区分管理员、收银员、配送员等角色,提供精细化的权限控制;
- 异常处理流程:完善订单异常、骑手异常的处理流程,支持记录异常原因和处理结果;
- 评价体系:增加客户评价功能,支持对配送服务的评分和留言,提升服务质量;
- 优惠券集成:对接优惠券系统,支持订单金额的优惠计算和抵扣;
- 超时提醒:增加订单超时提醒功能,避免配送延误,提升客户体验。
外卖配送管理系统作为餐饮商家数字化运营的核心工具,其跨端适配的关键在于行业特性、操作效率、体验一致性的三位一体。这份 React Native 实现的外卖配送管理组件,通过强类型领域建模、智能骑手匹配逻辑、行业化视觉设计,构建了高效的移动端外卖管理体验;而鸿蒙 ArkTS 端的适配实现,则验证了跨端开发中"逻辑复用、体验统一"的核心原则。
- 外卖配送系统的类型设计需包含订单全维度信息(客户、地址、菜品、金额、状态)和骑手核心信息(距离、评分、在线状态),保证配送管理的完整性;
- 订单创建逻辑需包含必填字段校验、数据格式化、状态初始化等核心步骤,保证订单数据的有效性和规范性;
- 骑手匹配算法需基于可用性筛选和距离最优原则,支持人工干预和智能分配两种模式,提升配送效率;
- 视觉设计采用暖色系主色调,订单状态通过不同色系区分,符合餐饮外卖的行业视觉认知和快速识别需求;
- 跨端适配的核心是外卖配送的行业逻辑复用 + 平台特性适配,无需重写核心业务逻辑;
- 表单设计需贴合商家录入习惯,支持多行地址输入、电话/金额专用键盘,提升订单创建效率;
- 操作按钮需根据订单状态动态展示,避免无效操作,符合外卖配送的流程逻辑;
- 骑手列表需展示距离、评分、在线状态等核心信息,为商家的骑手分配决策提供数据支撑。
真实演示案例代码:
// App.tsx
import React, { useState } from 'react';
import { SafeAreaView, View, Text, StyleSheet, TouchableOpacity, ScrollView, Dimensions, Alert, TextInput } from 'react-native';
// Base64 图标库
const ICONS_BASE64 = {
restaurant: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
bike: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
order: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
location: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
time: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
money: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
check: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
phone: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
};
const { width, height } = Dimensions.get('window');
// 外卖订单类型
type FoodOrder = {
id: string;
customerName: string;
customerPhone: string;
deliveryAddress: string;
items: string[];
totalAmount: number;
orderTime: string;
estimatedDelivery: string;
status: '待接单' | '已接单' | '配送中' | '已完成';
riderId: string;
};
// 骑手信息类型
type Rider = {
id: string;
name: string;
phone: string;
vehicleType: string;
licensePlate: string;
distance: number;
rating: number;
isAvailable: boolean;
};
// 餐饮商家外卖配送应用组件
const FoodDeliveryApp: React.FC = () => {
const [orders, setOrders] = useState<FoodOrder[]>([
{
id: '1',
customerName: '张先生',
customerPhone: '138****1234',
deliveryAddress: '北京市朝阳区某某小区12号楼',
items: ['宫保鸡丁', '麻婆豆腐', '米饭'],
totalAmount: 68,
orderTime: '2023-12-01 18:30',
estimatedDelivery: '2023-12-01 19:00',
status: '待接单',
riderId: ''
},
{
id: '2',
customerName: '李女士',
customerPhone: '139****5678',
deliveryAddress: '上海市浦东新区某写字楼',
items: ['红烧肉', '青菜', '汤'],
totalAmount: 56,
orderTime: '2023-12-01 19:15',
estimatedDelivery: '2023-12-01 19:45',
status: '已接单',
riderId: 'r1'
}
]);
const [riders] = useState<Rider[]>([
{
id: 'r1',
name: '王骑手',
phone: '138****1234',
vehicleType: '电动车',
licensePlate: '京A12345',
distance: 1.2,
rating: 4.8,
isAvailable: true
},
{
id: 'r2',
name: '刘骑手',
phone: '139****5678',
vehicleType: '摩托车',
licensePlate: '沪B67890',
distance: 2.5,
rating: 4.9,
isAvailable: false
}
]);
const [newOrder, setNewOrder] = useState({
customerName: '',
customerPhone: '',
deliveryAddress: '',
items: '',
totalAmount: ''
});
const getStatusColor = (status: string) => {
switch (status) {
case '待接单': return '#f59e0b';
case '已接单': return '#3b82f6';
case '配送中': return '#8b5cf6';
case '已完成': return '#10b981';
default: return '#6b7280';
}
};
const handleCreateOrder = () => {
if (!newOrder.customerName || !newOrder.customerPhone || !newOrder.deliveryAddress) {
Alert.alert('提示', '请填写完整的订单信息');
return;
}
const order: FoodOrder = {
id: (orders.length + 1).toString(),
customerName: newOrder.customerName,
customerPhone: newOrder.customerPhone,
deliveryAddress: newOrder.deliveryAddress,
items: newOrder.items.split(',').map(item => item.trim()),
totalAmount: parseFloat(newOrder.totalAmount) || 0,
orderTime: new Date().toLocaleString('zh-CN'),
estimatedDelivery: '待分配',
status: '待接单',
riderId: ''
};
setOrders([...orders, order]);
setNewOrder({
customerName: '',
customerPhone: '',
deliveryAddress: '',
items: '',
totalAmount: ''
});
Alert.alert('成功', '外卖订单创建成功!');
};
const handleAssignRider = (orderId: string) => {
const order = orders.find(o => o.id === orderId);
if (order) {
const availableRiders = riders.filter(r => r.isAvailable);
if (availableRiders.length > 0) {
Alert.alert(
'分配骑手',
`为订单${order.id}找到了${availableRiders.length}位附近骑手`,
[
{
text: '查看详情',
onPress: () => Alert.alert(
'骑手信息',
availableRiders.map(rider =>
`${rider.name} - ${rider.vehicleType} - 距离${rider.distance}km - 评分${rider.rating}`
).join('\n\n')
)
},
{
text: '分配最近骑手',
onPress: () => {
const nearestRider = availableRiders.reduce((prev, current) =>
prev.distance < current.distance ? prev : current
);
setOrders(orders.map(o =>
o.id === orderId
? { ...o, status: '已接单', riderId: nearestRider.id }
: o
));
Alert.alert('成功', `已分配给${nearestRider.name}`);
}
},
{ text: '取消', style: 'cancel' }
]
);
} else {
Alert.alert('提示', '暂时没有可用骑手,请稍后再试');
}
}
};
const handleContactRider = (riderId: string) => {
const rider = riders.find(r => r.id === riderId);
if (rider) {
Alert.alert('联系骑手', `拨打 ${rider.phone}`, [
{ text: '取消', style: 'cancel' },
{ text: '拨打', onPress: () => Alert.alert('提示', '正在拨打电话...') }
]);
}
};
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.orderCard}>
<Text style={styles.sectionTitle}>创建外卖订单</Text>
<TextInput
style={styles.input}
placeholder="客户姓名"
value={newOrder.customerName}
onChangeText={(text) => setNewOrder({...newOrder, customerName: text})}
/>
<TextInput
style={styles.input}
placeholder="客户电话"
value={newOrder.customerPhone}
onChangeText={(text) => setNewOrder({...newOrder, customerPhone: text})}
keyboardType="phone-pad"
/>
<TextInput
style={[styles.input, styles.textArea]}
placeholder="配送地址"
value={newOrder.deliveryAddress}
onChangeText={(text) => setNewOrder({...newOrder, deliveryAddress: text})}
multiline
numberOfLines={3}
/>
<TextInput
style={styles.input}
placeholder="菜品(用逗号分隔)"
value={newOrder.items}
onChangeText={(text) => setNewOrder({...newOrder, items: text})}
/>
<TextInput
style={styles.input}
placeholder="订单金额(元)"
value={newOrder.totalAmount}
onChangeText={(text) => setNewOrder({...newOrder, totalAmount: text})}
keyboardType="numeric"
/>
<TouchableOpacity
style={styles.createButton}
onPress={handleCreateOrder}
>
<Text style={styles.createButtonText}>创建订单</Text>
</TouchableOpacity>
</View>
{/* 订单列表 */}
<View style={styles.ordersCard}>
<Text style={styles.sectionTitle}>外卖订单</Text>
{orders.map(order => (
<View key={order.id} style={styles.orderItem}>
<View style={styles.orderHeader}>
<Text style={styles.customerName}>{order.customerName}</Text>
<View style={[
styles.statusBadge,
{ backgroundColor: getStatusColor(order.status) }
]}>
<Text style={styles.statusText}>{order.status}</Text>
</View>
</View>
<View style={styles.orderDetails}>
<View style={styles.detailRow}>
<Text style={styles.detailLabel}>电话:</Text>
<Text style={styles.detailValue}>{order.customerPhone}</Text>
</View>
<View style={styles.detailRow}>
<Text style={styles.detailLabel}>地址:</Text>
<Text style={styles.detailValue}>{order.deliveryAddress}</Text>
</View>
<View style={styles.detailRow}>
<Text style={styles.detailLabel}>菜品:</Text>
<Text style={styles.detailValue}>{order.items.join('、')}</Text>
</View>
<View style={styles.detailRow}>
<Text style={styles.detailLabel}>金额:</Text>
<Text style={styles.detailValue}>¥{order.totalAmount}</Text>
</View>
</View>
<View style={styles.orderActions}>
{order.status === '待接单' && (
<TouchableOpacity
style={styles.assignButton}
onPress={() => handleAssignRider(order.id)}
>
<Text style={styles.assignButtonText}>分配骑手</Text>
</TouchableOpacity>
)}
{order.riderId && (
<TouchableOpacity
style={styles.contactButton}
onPress={() => handleContactRider(order.riderId)}
>
<Text style={styles.contactButtonText}>联系骑手</Text>
</TouchableOpacity>
)}
<TouchableOpacity
style={styles.detailButton}
onPress={() => Alert.alert('详情', `订单ID: ${order.id}`)}
>
<Text style={styles.detailButtonText}>查看详情</Text>
</TouchableOpacity>
</View>
</View>
))}
</View>
{/* 可用骑手 */}
<View style={styles.ridersCard}>
<Text style={styles.sectionTitle}>附近骑手</Text>
{riders.map(rider => (
<View key={rider.id} style={styles.riderItem}>
<View style={styles.riderInfo}>
<Text style={styles.riderName}>{rider.name}</Text>
<Text style={styles.riderVehicle}>{rider.vehicleType} • {rider.licensePlate}</Text>
<View style={styles.riderRating}>
<Text>⭐ {rider.rating}</Text>
<Text style={styles.distanceText}>距离: {rider.distance}km</Text>
</View>
</View>
<View style={styles.riderStatus}>
<View style={[
styles.availabilityIndicator,
{ backgroundColor: rider.isAvailable ? '#10b981' : '#6b7280' }
]} />
<Text style={styles.statusText}>
{rider.isAvailable ? '在线' : '离线'}
</Text>
</View>
<TouchableOpacity
style={styles.contactButton}
onPress={() => handleContactRider(rider.id)}
>
<Text style={styles.contactButtonText}>联系</Text>
</TouchableOpacity>
</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: '#fffbeb',
},
header: {
flexDirection: 'column',
padding: 16,
backgroundColor: '#ffffff',
borderBottomWidth: 1,
borderBottomColor: '#fed7aa',
},
title: {
fontSize: 20,
fontWeight: 'bold',
color: '#9a3412',
marginBottom: 4,
},
subtitle: {
fontSize: 14,
color: '#ea580c',
},
content: {
flex: 1,
marginTop: 12,
},
orderCard: {
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: '#9a3412',
marginBottom: 12,
},
input: {
borderWidth: 1,
borderColor: '#fed7aa',
borderRadius: 8,
padding: 12,
fontSize: 14,
backgroundColor: '#fff7ed',
marginBottom: 12,
},
textArea: {
minHeight: 80,
textAlignVertical: 'top',
},
createButton: {
backgroundColor: '#f97316',
paddingVertical: 14,
borderRadius: 8,
alignItems: 'center',
},
createButtonText: {
color: '#ffffff',
fontSize: 16,
fontWeight: '600',
},
ordersCard: {
backgroundColor: '#ffffff',
marginHorizontal: 16,
marginBottom: 12,
borderRadius: 12,
padding: 16,
elevation: 2,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
},
orderItem: {
padding: 12,
borderBottomWidth: 1,
borderBottomColor: '#fed7aa',
},
orderHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 8,
},
customerName: {
fontSize: 16,
fontWeight: '600',
color: '#9a3412',
},
statusBadge: {
paddingHorizontal: 8,
paddingVertical: 4,
borderRadius: 12,
},
statusText: {
fontSize: 12,
color: '#ffffff',
fontWeight: '500',
},
orderDetails: {
marginBottom: 12,
},
detailRow: {
flexDirection: 'row',
marginBottom: 4,
},
detailLabel: {
fontSize: 12,
color: '#64748b',
width: 50,
},
detailValue: {
fontSize: 12,
color: '#9a3412',
flex: 1,
},
orderActions: {
flexDirection: 'row',
justifyContent: 'flex-end',
},
assignButton: {
backgroundColor: '#10b981',
paddingHorizontal: 12,
paddingVertical: 6,
borderRadius: 16,
marginRight: 8,
},
assignButtonText: {
color: '#ffffff',
fontSize: 12,
},
contactButton: {
backgroundColor: '#fed7aa',
paddingHorizontal: 12,
paddingVertical: 6,
borderRadius: 16,
marginRight: 8,
},
contactButtonText: {
color: '#f97316',
fontSize: 12,
},
detailButton: {
backgroundColor: '#fed7aa',
paddingHorizontal: 12,
paddingVertical: 6,
borderRadius: 16,
},
detailButtonText: {
color: '#f97316',
fontSize: 12,
},
ridersCard: {
backgroundColor: '#ffffff',
marginHorizontal: 16,
marginBottom: 12,
borderRadius: 12,
padding: 16,
elevation: 2,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
},
riderItem: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingVertical: 12,
borderBottomWidth: 1,
borderBottomColor: '#fed7aa',
},
riderInfo: {
flex: 1,
},
riderName: {
fontSize: 16,
fontWeight: '600',
color: '#9a3412',
marginBottom: 4,
},
riderVehicle: {
fontSize: 14,
color: '#64748b',
marginBottom: 4,
},
riderRating: {
flexDirection: 'row',
justifyContent: 'space-between',
},
distanceText: {
fontSize: 12,
color: '#64748b',
},
riderStatus: {
alignItems: 'center',
marginRight: 12,
},
availabilityIndicator: {
width: 12,
height: 12,
borderRadius: 6,
marginBottom: 4,
},
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: '#fed7aa',
paddingVertical: 12,
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
},
navItem: {
alignItems: 'center',
flex: 1,
},
activeNavItem: {
paddingTop: 4,
borderTopWidth: 2,
borderTopColor: '#f97316',
},
navIcon: {
fontSize: 20,
color: '#94a3b8',
marginBottom: 4,
},
activeNavIcon: {
color: '#f97316',
},
navText: {
fontSize: 12,
color: '#94a3b8',
},
activeNavText: {
color: '#f97316',
fontWeight: '500',
},
});
export default FoodDeliveryApp;

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

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

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

本文介绍了一个基于React Native的跨平台外卖配送应用架构设计。系统采用函数式组件和TypeScript类型系统,定义了FoodOrder和Rider核心数据类型,管理订单全生命周期和骑手信息。通过useState实现订单创建、骑手分配和状态更新等核心业务流程,采用单向数据流模式确保数据一致性。文章详细阐述了状态管理机制、业务逻辑实现和性能优化策略,并针对鸿蒙平台提供了适配建议。该系统设计模块化、可扩展,支持餐饮商家高效管理外卖配送业务。
欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。
更多推荐
所有评论(0)