基于React Native鸿蒙跨平台承运商计费规则管理、多运输需求建模、报价结果结构化,实现了批量运输报价比较的完整功能
本文介绍了基于React Native开发的批量运输报价应用,采用函数式组件架构实现跨平台支持。核心功能包括运输需求管理、智能报价计算和承运商比较,使用TypeScript确保类型安全。文章详细解析了承运商、运输需求和报价的数据结构设计,以及状态管理和业务逻辑的实现。特别针对鸿蒙系统进行了兼容性优化,包括组件适配和性能调优。最后提出了类型增强、状态持久化和组件化等改进方向,为构建高效的物流管理系统
欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。
架构
本项目采用React Native函数式组件架构,以BulkTransportQuotesApp为核心组件,实现了批量运输报价比较的完整功能流程。架构设计遵循模块化原则,将数据结构、状态管理和业务逻辑清晰分离,便于维护和扩展。
核心技术栈
- React Native:跨平台移动应用开发框架,支持iOS、Android和鸿蒙系统
- TypeScript:提供类型安全,增强代码可维护性和开发体验
- Hooks API:使用useState进行状态管理,简化组件逻辑
- Flexbox:实现响应式布局,适配不同屏幕尺寸
- Base64图标:内置图标资源,减少网络请求,提升加载速度
- Dimensions API:获取屏幕尺寸,实现精细化布局控制
承运商类型(Carrier)
type Carrier = {
id: string;
name: string;
rating: number;
basePrice: number;
pricePerKg: number;
pricePerVolume: number;
deliveryTime: string;
services: string[];
};
该类型设计全面,包含了承运商的核心业务信息:
id:唯一标识符,确保数据唯一性name:公司名称,便于用户识别rating:评分信息,反映服务质量basePrice:基础运费,运输费用计算的起点pricePerKg:每公斤价格,重量费用计算依据pricePerVolume:每体积价格,体积费用计算依据deliveryTime:预计时效,帮助用户选择services:服务范围数组,明确公司能力边界
运输需求类型(TransportRequest)
type TransportRequest = {
id: string;
cargoType: string;
weight: number;
volume: number;
pickupLocation: string;
deliveryLocation: string;
deliveryDate: string;
status: '待报价' | '已报价' | '已确认' | '已完成';
};
运输需求类型设计合理,包含了批量运输的必要信息:
id:需求唯一标识cargoType:货物类型,影响运输方案weight:重量信息,直接影响运输成本volume:体积信息,对运输空间占用至关重要pickupLocation、deliveryLocation:起运地和目的地,影响运输路线deliveryDate:交货日期,影响运输计划status:状态字段,使用联合类型确保类型安全,清晰定义业务流程
报价类型(Quote)
type Quote = {
carrierId: string;
totalPrice: number;
deliveryTime: string;
breakdown: {
baseFee: number;
weightFee: number;
volumeFee: number;
};
};
报价类型设计详细,便于用户了解费用构成:
carrierId:关联承运商ID,建立数据关联totalPrice:总费用,用户最关心的核心信息deliveryTime:预计时效,与价格一同作为选择依据breakdown:费用明细,包含基础费用、重量费用和体积费用,提升透明度
核心状态
应用使用useState钩子管理四个核心状态:
carriers:承运商列表,使用常量状态transportRequests:运输需求列表,支持动态更新quotes:报价信息,使用对象存储,键为需求ID,值为报价数组selectedRequest:选中的运输需求,用于显示报价信息
状态更新机制
- 报价计算:通过calculateQuotes函数,根据运输需求计算各承运商报价
- 状态更新:通过setTransportRequests函数,更新运输需求状态
- 报价存储:通过setQuotes函数,将计算结果存储到对应需求ID下
- 选择管理:通过setSelectedRequest函数,管理当前选中的需求
状态管理
- 不可变数据模式:使用扩展运算符(
...)创建新状态,避免直接修改原状态 - 对象存储优化:使用对象存储报价信息,通过需求ID快速查找
- 批量更新:在状态更新时使用map方法批量处理,确保数据一致性
- 状态清理:在确认承运商后,清理选中状态,保持界面整洁
核心业务
- 运输需求管理:展示和管理多个运输需求
- 报价计算:根据需求信息计算各承运商报价
- 价格比较:展示各承运商报价,支持价格排序
- 承运商选择:用户选择合适的承运商
- 状态更新:更新运输需求状态为"已确认"
核心计算
const calculateQuotes = (requestId: string) => {
const request = transportRequests.find(r => r.id === requestId);
if (!request) return;
const calculatedQuotes = carriers.map(carrier => {
const weightFee = request.weight * carrier.pricePerKg;
const volumeFee = request.volume * carrier.pricePerVolume;
const totalPrice = carrier.basePrice + weightFee + volumeFee;
return {
carrierId: carrier.id,
totalPrice: Math.round(totalPrice),
deliveryTime: carrier.deliveryTime,
breakdown: {
baseFee: carrier.basePrice,
weightFee: Math.round(weightFee),
volumeFee: Math.round(volumeFee)
}
};
});
// 按价格排序
calculatedQuotes.sort((a, b) => a.totalPrice - b.totalPrice);
setQuotes(prev => ({ ...prev, [requestId]: calculatedQuotes }));
setSelectedRequest(requestId);
};
数据流
- 单向数据流:状态 → 视图 → 用户操作 → 状态更新
- 数据关联:通过ID字段建立不同数据类型之间的关联
- 业务逻辑封装:将复杂计算逻辑封装在专用函数中,提高代码可读性
- 用户交互反馈:使用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. 组件化
- 组件拆分:将大型组件拆分为更小的可复用组件,如CarrierCard、RequestItem、QuoteCard等
- 自定义Hook:提取重复的状态逻辑到自定义Hook,如useQuoteCalculator、useRequestManager等
- 高阶组件:使用高阶组件处理横切关注点,如错误边界、加载状态等
4. 业务逻辑
-
服务层分离:将报价计算逻辑分离到服务层,提高代码可测试性
-
错误处理:增强错误处理机制,提供更友好的错误提示
-
国际化支持:考虑添加国际化支持,提升应用的全球适配能力
-
列表优化:使用FlatList的性能优化特性,如getItemLayout、initialNumToRender等
-
计算优化:实现计算防抖,减少实时计算频率
-
Bundle优化:使用代码分割和Tree Shaking,减少应用体积
- 批量运输管理:实现了多个运输需求的同时管理,提高物流效率
- 智能报价计算:根据重量和体积自动计算各承运商报价,支持价格排序
- 详细费用明细:提供基础费用、重量费用和体积费用的详细 breakdown,提升透明度
- 状态追踪:实时更新运输需求状态,确保业务流程透明
- 类型安全:充分利用TypeScript的类型系统,提高代码质量和开发效率
- 响应式设计:使用Flexbox和Dimensions API,实现适配不同屏幕尺寸的布局
- 内置图标资源:使用Base64编码图标,减少网络依赖,提升加载速度
- 用户友好的交互:提供清晰的操作反馈和信息提示,提升用户体验
- 模块化架构:代码结构模块化,便于维护和扩展
本项目展示了如何使用React Native和TypeScript构建一个功能完整的批量运输报价比较应用。通过合理的架构设计、类型定义和状态管理,实现了跨平台的一致性体验。
随着React Native和鸿蒙系统的不断发展,跨端开发将会变得更加成熟和高效。未来可以考虑:
- 使用React Native 0.70+版本:利用新的架构特性,如Fabric渲染器和Turbo Modules,提升应用性能
- 探索鸿蒙原生能力:充分利用鸿蒙系统的分布式能力,实现多设备协同和更丰富的功能
- 引入现代化状态管理:使用Redux Toolkit或Zustand等现代状态管理库,简化状态管理
- 实现PWA支持:扩展应用的使用场景,支持Web平台
- 集成AI能力:引入AI技术,如智能路线规划、成本预测等,提升应用智能化水平
批量运输报价是大宗物流场景的核心需求,区别于小件快递计价,其系统设计需要兼顾多运输需求管理、批量报价计算、承运商多维度对比三大核心特性。本文将深度拆解基于 React Native 开发的批量运输报价应用,剖析其多维度计价逻辑、状态管理机制、承运商对比展示的技术实现思路,并提供完整的鸿蒙(HarmonyOS)ArkTS 跨端适配方案,为大宗物流类应用的跨端开发提供可落地的技术参考。
1. 贴合大宗物流场景
批量运输报价系统面向企业级大宗物流场景,其核心诉求是承运商计费规则管理、多运输需求建模、报价结果结构化,代码通过 TypeScript 类型系统构建了精准贴合大宗物流计价场景的领域模型,充分体现了批量运输报价的行业特性:
// 承运商类型
type Carrier = {
id: string;
name: string;
rating: number;
basePrice: number;
pricePerKg: number;
pricePerVolume: number;
deliveryTime: string;
services: string[];
};
// 运输需求类型
type TransportRequest = {
id: string;
cargoType: string;
weight: number;
volume: number;
pickupLocation: string;
deliveryLocation: string;
deliveryDate: string;
status: '待报价' | '已报价' | '已确认' | '已完成';
};
// 报价类型
type Quote = {
carrierId: string;
totalPrice: number;
deliveryTime: string;
breakdown: {
baseFee: number;
weightFee: number;
volumeFee: number;
};
};
模型设计的大宗物流场景适配性分析:
- 计费规则企业级适配:承运商模型包含基础价(basePrice)、重量单价(pricePerKg)、体积单价(pricePerVolume)等核心计费字段,且基础价设定为500+级别,精准匹配大宗物流"高基础费+按重量/体积计费"的计价规则,区别于小件快递的低基础费模式;
- 运输需求多维度建模:运输需求模型涵盖货物类型、重量、体积、收发地址、配送日期、状态等全维度字段,重量单位以kg为量级(1500kg/3200kg)、体积以立方米为单位(8.5m³/15.2m³),完全贴合大宗货物的计量特性;
- 状态流转标准化:运输需求状态采用枚举式设计(待报价/已报价/已确认/已完成),构建完整的物流业务流程闭环,符合企业级物流管理的状态管控需求;
- 报价结果精细化:报价模型不仅包含总价,还拆分基础费、重量费、体积费明细,且通过 carrierId 关联承运商,保证报价溯源性,满足企业用户对成本构成的精细化管理需求;
- 服务能力差异化:承运商模型增加 services 字段(整车运输/零担配送/仓储服务),支持按服务类型筛选对比,解决大宗物流中服务能力匹配的核心诉求;
- 数据格式化适配:报价总价使用
toLocaleString()格式化,适配大宗物流高金额展示的阅读习惯,避免长数字串的视觉疲劳; - 类型安全保障:状态字段采用字面量类型约束,避免非法状态值输入,保证企业级物流系统的数据严谨性;
- 评价体系完善化:承运商模型包含 rating 字段,支持按服务评分多维度对比,符合企业用户对物流服务商综合评估的需求。
2. 批量报价的精准计算
批量运输报价的核心价值在于多运输需求的批量计价、报价结果的状态关联、承运商选择的流程化,代码通过轻量化但专业的算法实现了大宗物流的核心计价逻辑:
(1)批量报价计算
大宗物流的核心痛点是多运输需求的快速报价,代码实现了基于运输需求ID的精准计价逻辑,支持多需求独立报价和状态管理:
const calculateQuotes = (requestId: string) => {
const request = transportRequests.find(r => r.id === requestId);
if (!request) return;
const calculatedQuotes = carriers.map(carrier => {
const weightFee = request.weight * carrier.pricePerKg;
const volumeFee = request.volume * carrier.pricePerVolume;
const totalPrice = carrier.basePrice + weightFee + volumeFee;
return {
carrierId: carrier.id,
totalPrice: Math.round(totalPrice),
deliveryTime: carrier.deliveryTime,
breakdown: {
baseFee: carrier.basePrice,
weightFee: Math.round(weightFee),
volumeFee: Math.round(volumeFee)
}
};
});
// 按价格排序
calculatedQuotes.sort((a, b) => a.totalPrice - b.totalPrice);
setQuotes(prev => ({ ...prev, [requestId]: calculatedQuotes }));
setSelectedRequest(requestId);
};
批量报价算法的行业适配性:
- 需求精准定位:通过 requestId 精准定位待报价的运输需求,支持多需求独立报价,符合企业用户批量处理物流需求的场景;
- 计价公式专业化:严格按照大宗物流"基础费 + 重量×单价 + 体积×单价"的复合计价公式,基础费占比合理,匹配大宗运输的成本构成;
- 报价结果格式化:使用
Math.round()对计算结果取整,避免小数点后多位的冗余展示,符合企业财务对账的精度要求; - 报价排序优化:按总价升序排列报价结果,自动标注最优选项,提升企业用户比价效率,符合大宗物流成本优先的决策逻辑;
- 状态关联机制:报价结果通过对象键值对({[requestId]: quotes})与运输需求ID关联,保证多需求报价结果的独立存储和管理;
- 异常防护机制:先校验运输需求是否存在,避免空指针异常,保证企业级应用的稳定性;
- 选择状态同步:计算完成后自动选中当前报价的需求ID,保证UI展示的即时性,提升操作流畅度。
(2)状态可视化
大宗物流的核心管理需求是状态可视化,代码实现了基于状态的颜色映射和流程化操作,符合企业级物流管理的视觉认知习惯:
const getStatusColor = (status: string) => {
switch (status) {
case '待报价': return '#f59e0b';
case '已报价': return '#3b82f6';
case '已确认': return '#8b5cf6';
case '已完成': return '#10b981';
default: return '#6b7280';
}
};
const handleSelectCarrier = (requestId: string, carrierId: string) => {
const request = transportRequests.find(r => r.id === requestId);
const quote = quotes[requestId]?.find(q => q.carrierId === carrierId);
const carrier = carriers.find(c => c.id === carrierId);
if (request && quote && carrier) {
Alert.alert(
'确认承运商',
`运输需求: ${request.cargoType}\n` +
`承运商: ${carrier.name}\n` +
`总费用: ¥${quote.totalPrice.toLocaleString()}\n` +
`预计时效: ${quote.deliveryTime}\n\n` +
`费用明细:\n` +
`基础费用: ¥${quote.breakdown.baseFee}\n` +
`重量费用: ¥${quote.breakdown.weightFee}\n` +
`体积费用: ¥${quote.breakdown.volumeFee}`,
[
{ text: '取消', style: 'cancel' },
{
text: '确认选择',
onPress: () => {
setTransportRequests(transportRequests.map(r =>
r.id === requestId
? { ...r, status: '已确认' }
: r
));
setSelectedRequest('');
Alert.alert('成功', '承运商已确认!');
}
}
]
);
}
};
状态管理逻辑的行业适配性:
- 视觉化状态映射:不同业务状态对应不同色系(待报价-黄色、已报价-蓝色、已确认-紫色、已完成-绿色),符合企业级系统的视觉认知规范,便于快速识别业务进度;
- 选择流程完整化:承运商选择包含信息校验、详情展示、确认操作、状态更新全流程,模拟真实的企业物流采购决策流程;
- 数据校验完善化:多重校验运输需求、报价、承运商信息的存在性,避免空值导致的流程中断,保证企业级应用的稳定性;
- 金额格式化展示:使用
toLocaleString()格式化大额运费,符合企业财务数据的展示习惯,提升数据可读性; - 状态原子化更新:仅更新选中的运输需求状态,保持其他需求状态不变,符合批量处理的业务逻辑;
- 操作反馈即时化:状态更新后清空选中状态并给出成功提示,符合企业用户的操作反馈预期;
- 信息展示结构化:按"运输需求-承运商-总价-时效-明细"的逻辑展示选择结果,符合企业决策的信息获取优先级。
3.大宗物流场景
批量运输报价系统的视觉设计围绕企业级、批量处理、多维度对比三个核心维度展开,贴合大宗物流的业务特性和企业用户操作习惯:
(1)大宗物流场景
- 行业化色调体系:采用青蓝色系(
#06b6d4)为主色调,契合大宗物流专业、严谨的品牌属性,区别于消费级应用的视觉风格; - 批量列表布局:运输需求采用卡片式列表布局,按需求维度分区展示,符合企业用户批量查看、处理物流需求的操作习惯;
- 状态标签化展示:业务状态以彩色徽章形式展示,视觉突出,便于快速识别各需求的处理进度,符合企业级系统的信息密度要求;
- 报价信息结构化:各承运商报价采用卡片式布局,价格突出展示,同时展示服务类型、评分、时效等多维度信息,满足企业综合评估需求;
- 操作按钮场景化:待报价状态显示"获取报价"按钮,已报价状态自动隐藏,符合业务流程的操作逻辑;
- 费用明细透明化:基础费、重量费、体积费横向排列,便于快速查看成本构成,符合企业成本管控的核心诉求;
- 承运商信息标准化:展示名称、评分、费率、服务项目等核心信息,便于企业用户按多维度筛选对比;
- 底部导航场景化:按需求、报价、订单、我的分类,贴合企业物流管理的核心业务流程;
- 安全区域适配:使用 SafeAreaView 适配异形屏,保证企业级应用的界面完整性;
- 卡片层级化设计:所有功能模块采用卡片式设计+轻微阴影,提升界面层次感,符合现代企业级系统的设计趋势。
(2)批量处理
- 批量报价独立化:每个运输需求独立报价,避免批量计算的性能损耗,符合企业按需处理的业务逻辑;
- 报价结果关联化:报价结果与运输需求ID关联,支持多需求报价结果的独立展示和管理;
- 状态视觉差异化:不同业务状态采用不同色系标签,便于快速识别处理进度,提升批量处理效率;
- 选择反馈可视化:选中的报价卡片视觉突出,便于确认选择结果,符合企业用户的操作习惯;
- 明细查看便捷化:点击报价卡片可查看完整费用明细,满足企业对成本构成的精细化管理需求;
- 批量操作轻量化:底部导航固定展示核心功能,便于批量处理过程中的功能快速切换;
- 价格排序智能化:自动按价格升序排列报价结果,符合企业成本优先的决策逻辑;
- 滚动体验流畅化:报价列表支持滚动,适配多承运商比价的展示需求;
- 使用说明简洁化:提炼核心操作步骤,降低企业用户的学习成本,符合企业级系统的易用性原则。
将 React Native 批量运输报价系统迁移至鸿蒙平台,核心是基于 ArkTS + ArkUI 实现类型系统、状态管理、业务逻辑、视觉交互的对等还原,同时适配鸿蒙的组件特性和布局范式,保证大宗物流报价体验的一致性和专业性。
1. 架构
鸿蒙端适配遵循类型复用、逻辑对等、体验统一的原则,批量运输报价的核心业务逻辑和视觉规范100%复用,仅需适配平台特有API和组件语法,确保大宗物流应用的跨端体验一致性:
@Entry
@Component
struct BulkTransportQuotesApp {
// 类型定义:对等实现 TypeScript 类型 → 接口定义
interface Carrier {
id: string;
name: string;
rating: number;
basePrice: number;
pricePerKg: number;
pricePerVolume: number;
deliveryTime: string;
services: string[];
}
interface TransportRequest {
id: string;
cargoType: string;
weight: number;
volume: number;
pickupLocation: string;
deliveryLocation: string;
deliveryDate: string;
status: '待报价' | '已报价' | '已确认' | '已完成';
}
interface Quote {
carrierId: string;
totalPrice: number;
deliveryTime: string;
breakdown: {
baseFee: number;
weightFee: number;
volumeFee: number;
};
}
// 状态管理:对等实现 useState → @State
@State carriers: Carrier[] = [/* 初始承运商数据 */];
@State transportRequests: TransportRequest[] = [/* 初始运输需求数据 */];
@State quotes: {[key: string]: Quote[]} = {};
@State selectedRequest: string = '';
// 业务逻辑:完全复用 RN 端的大宗物流计费核心逻辑
calculateQuotes(requestId: string): void {/* 批量报价计算 */}
getStatusColor(status: string): string {/* 状态颜色映射 */}
handleSelectCarrier(requestId: string, carrierId: string): void {/* 承运商选择 */}
// 页面构建:镜像 RN 端布局结构,适配鸿蒙组件特性
build() {
Column() {
// 头部区域
// 运输需求列表
// 报价比较区域
// 承运商信息
// 使用说明
// 底部导航
}
}
}
| React Native 特性 | 鸿蒙 ArkUI 对应实现 | 大宗物流场景适配关键说明 |
|---|---|---|
| TypeScript 类型定义 | TypeScript 接口定义 | 承运商、运输需求、报价类型完全复用,保证大宗物流计费数据结构一致性 |
useState |
@State 装饰器 |
承运商、运输需求、报价结果、选中状态的管理逻辑完全复用,保持批量报价流程一致性 |
| 批量报价算法 | 函数逻辑完全复用 | 计价公式、价格取整、排序规则一致,保证大宗物流报价结果的准确性 |
TouchableOpacity |
Button/Column + onClick |
所有可点击区域通过 onClick 事件实现,保持大宗物流交互一致性 |
Alert.alert |
AlertDialog.show |
报价提示、承运商选择等弹窗逻辑对等,符合企业用户的操作习惯 |
StyleSheet |
链式样式 | 青蓝色系主色调、报价卡片样式等大宗物流场景视觉规范100%复用 |
Array.map |
ForEach 组件 |
运输需求/承运商/报价列表渲染逻辑一致,适配批量处理的展示需求 |
ScrollView |
Scroll 组件 |
滚动容器语法差异,功能一致,适配大批量运输需求展示 |
| 状态颜色映射 | 函数逻辑完全复用 | 状态-颜色映射规则一致,保证企业级系统的视觉认知一致性 |
| 金额格式化 | toLocaleString() |
方法完全复用,保证大宗物流高金额展示的可读性 |
| 底部导航 | Position.Fixed |
导航栏定位语法差异,效果一致,保证企业应用的操作便捷性 |
3. 鸿蒙代码
// 鸿蒙 ArkTS 完整实现 - 批量运输报价系统
@Entry
@Component
struct BulkTransportQuotesApp {
// 类型定义
interface Carrier {
id: string;
name: string;
rating: number;
basePrice: number;
pricePerKg: number;
pricePerVolume: number;
deliveryTime: string;
services: string[];
}
interface TransportRequest {
id: string;
cargoType: string;
weight: number;
volume: number;
pickupLocation: string;
deliveryLocation: string;
deliveryDate: string;
status: '待报价' | '已报价' | '已确认' | '已完成';
}
interface Quote {
carrierId: string;
totalPrice: number;
deliveryTime: string;
breakdown: {
baseFee: number;
weightFee: number;
volumeFee: number;
};
}
// 状态管理
@State carriers: Carrier[] = [
{
id: 'c1',
name: '顺风物流',
rating: 4.8,
basePrice: 500,
pricePerKg: 3.5,
pricePerVolume: 150,
deliveryTime: '2-3天',
services: ['整车运输', '零担配送', '仓储服务']
},
{
id: 'c2',
name: '德邦物流',
rating: 4.6,
basePrice: 400,
pricePerKg: 3.0,
pricePerVolume: 120,
deliveryTime: '3-5天',
services: ['整车运输', '零担配送']
},
{
id: 'c3',
name: '天地华宇',
rating: 4.5,
basePrice: 350,
pricePerKg: 2.8,
pricePerVolume: 100,
deliveryTime: '4-7天',
services: ['整车运输', '仓储服务']
}
];
@State transportRequests: TransportRequest[] = [
{
id: '1',
cargoType: '电子产品',
weight: 1500,
volume: 8.5,
pickupLocation: '深圳市南山区',
deliveryLocation: '上海市浦东新区',
deliveryDate: '2023-12-20',
status: '待报价'
},
{
id: '2',
cargoType: '机械设备',
weight: 3200,
volume: 15.2,
pickupLocation: '广州市白云区',
deliveryLocation: '北京市朝阳区',
deliveryDate: '2023-12-22',
status: '已报价'
}
];
@State quotes: {[key: string]: Quote[]} = {};
@State selectedRequest: string = '';
// 计算指定运输需求的所有承运商报价
calculateQuotes(requestId: string): void {
const request = this.transportRequests.find(r => r.id === requestId);
if (!request) return;
const calculatedQuotes = this.carriers.map(carrier => {
const weightFee = request.weight * carrier.pricePerKg;
const volumeFee = request.volume * carrier.pricePerVolume;
const totalPrice = carrier.basePrice + weightFee + volumeFee;
return {
carrierId: carrier.id,
totalPrice: Math.round(totalPrice),
deliveryTime: carrier.deliveryTime,
breakdown: {
baseFee: carrier.basePrice,
weightFee: Math.round(weightFee),
volumeFee: Math.round(volumeFee)
}
};
});
// 按价格排序
calculatedQuotes.sort((a, b) => a.totalPrice - b.totalPrice);
this.quotes = { ...this.quotes, [requestId]: calculatedQuotes };
this.selectedRequest = requestId;
}
// 获取状态对应的颜色
getStatusColor(status: string): string {
switch (status) {
case '待报价': return '#f59e0b';
case '已报价': return '#3b82f6';
case '已确认': return '#8b5cf6';
case '已完成': return '#10b981';
default: return '#6b7280';
}
}
// 选择承运商
handleSelectCarrier(requestId: string, carrierId: string): void {
const request = this.transportRequests.find(r => r.id === requestId);
const quote = this.quotes[requestId]?.find(q => q.carrierId === carrierId);
const carrier = this.carriers.find(c => c.id === carrierId);
if (request && quote && carrier) {
AlertDialog.show({
title: '确认承运商',
message: `运输需求: ${request.cargoType}\n` +
`承运商: ${carrier.name}\n` +
`总费用: ¥${quote.totalPrice.toLocaleString()}\n` +
`预计时效: ${quote.deliveryTime}\n\n` +
`费用明细:\n` +
`基础费用: ¥${quote.breakdown.baseFee}\n` +
`重量费用: ¥${quote.breakdown.weightFee}\n` +
`体积费用: ¥${quote.breakdown.volumeFee}`,
cancel: { value: '取消' },
confirm: {
value: '确认选择',
action: () => {
this.transportRequests = this.transportRequests.map(r =>
r.id === requestId
? { ...r, status: '已确认' }
: r
);
this.selectedRequest = '';
AlertDialog.show({
title: '成功',
message: '承运商已确认!',
confirm: { value: '确定' }
});
}
}
});
}
}
build() {
Column()
.flex(1)
.backgroundColor('#ecfeff')
.safeArea(true) {
// 头部区域
Column()
.padding(16)
.backgroundColor('#ffffff')
.borderBottom({ width: 1, color: '#a5f3fc' }) {
Text('批量运输报价')
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor('#083344')
.marginBottom(4);
Text('获取多家承运商实时报价')
.fontSize(14)
.fontColor('#06b6d4');
}
// 滚动内容区
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('#083344')
.marginBottom(12);
// 运输需求列表
ForEach(this.transportRequests, (request: TransportRequest) => {
Column()
.padding(12)
.borderBottom({ width: 1, color: '#a5f3fc' }) {
// 需求头部
Row()
.justifyContent(FlexAlign.SpaceBetween)
.alignItems(ItemAlign.Center)
.marginBottom(8) {
Text(request.cargoType)
.fontSize(16)
.fontWeight(FontWeight.SemiBold)
.fontColor('#083344');
Column()
.paddingLeft(8)
.paddingRight(8)
.paddingTop(4)
.paddingBottom(4)
.borderRadius(12)
.backgroundColor(this.getStatusColor(request.status)) {
Text(request.status)
.fontSize(12)
.fontColor(Color.White)
.fontWeight(FontWeight.Medium);
}
}
// 需求详情
Column()
.marginBottom(12) {
ForEach([
{ label: '重量:', value: `${request.weight}kg` },
{ label: '体积:', value: `${request.volume}m³` },
{ label: '起运:', value: request.pickupLocation },
{ label: '送达:', value: request.deliveryLocation }
], (item) => {
Row()
.marginBottom(4) {
Text(item.label)
.fontSize(12)
.fontColor('#64748b')
.width(50);
Text(item.value)
.fontSize(12)
.fontColor('#083344')
.flexGrow(1);
}
})
}
// 需求操作按钮
Row()
.justifyContent(FlexAlign.End) {
if (request.status === '待报价') {
Button()
.backgroundColor('#06b6d4')
.paddingLeft(12)
.paddingRight(12)
.paddingTop(6)
.paddingBottom(6)
.borderRadius(16)
.marginRight(8)
.onClick(() => this.calculateQuotes(request.id)) {
Text('获取报价')
.fontColor(Color.White)
.fontSize(12);
}
}
Button()
.backgroundColor('#cffafe')
.paddingLeft(12)
.paddingRight(12)
.paddingTop(6)
.paddingBottom(6)
.borderRadius(16)
.onClick(() => {
AlertDialog.show({
title: '详情',
message: `需求ID: ${request.id}`,
confirm: { value: '确定' }
});
}) {
Text('查看详情')
.fontColor('#06b6d4')
.fontSize(12);
}
}
}
})
}
// 报价比较区域(选中需求时显示)
if (this.selectedRequest && this.quotes[this.selectedRequest]) {
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 }) {
// 报价头部
Row()
.justifyContent(FlexAlign.SpaceBetween)
.alignItems(ItemAlign.Center)
.marginBottom(12) {
Text('报价比较')
.fontSize(16)
.fontWeight(FontWeight.SemiBold)
.fontColor('#083344');
Text('✕')
.fontSize(18)
.fontColor('#64748b')
.onClick(() => this.selectedRequest = '');
}
// 报价列表
ForEach(this.quotes[this.selectedRequest], (quote: Quote, index: number) => {
const carrier = this.carriers.find(c => c.id === quote.carrierId);
Column()
.backgroundColor('#f0fdfa')
.borderRadius(8)
.padding(12)
.marginBottom(8)
.onClick(() => this.handleSelectCarrier(this.selectedRequest, quote.carrierId)) {
// 报价头部
Row()
.justifyContent(FlexAlign.SpaceBetween)
.alignItems(ItemAlign.Center)
.marginBottom(8) {
Column()
.flexGrow(1) {
Text(carrier?.name || '未知承运商')
.fontSize(14)
.fontWeight(FontWeight.Medium)
.fontColor('#083344')
.marginBottom(2);
Row()
.alignItems(ItemAlign.Center)
.marginBottom(4) {
Text(`⭐ ${carrier?.rating}`)
.fontSize(12)
.fontColor('#64748b');
Text(quote.deliveryTime)
.fontSize(12)
.fontColor('#64748b')
.marginLeft(8);
}
Text(`服务: ${carrier?.services.join('、')}`)
.fontSize(12)
.fontColor('#64748b');
}
Column()
.alignItems(ItemAlign.End) {
Text(`¥${quote.totalPrice.toLocaleString()}`)
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor('#06b6d4');
if (index === 0) {
Text('最优价')
.fontSize(12)
.fontColor('#10b981')
.backgroundColor('#d1fae5')
.paddingLeft(6)
.paddingRight(6)
.paddingTop(2)
.paddingBottom(2)
.borderRadius(10)
.marginTop(2);
}
}
}
// 费用明细
Row()
.justifyContent(FlexAlign.SpaceBetween) {
Text(`基础: ¥${quote.breakdown.baseFee}`)
.fontSize(12)
.fontColor('#64748b');
Text(`重量: ¥${quote.breakdown.weightFee}`)
.fontSize(12)
.fontColor('#64748b');
Text(`体积: ¥${quote.breakdown.volumeFee}`)
.fontSize(12)
.fontColor('#64748b');
}
}
})
}
}
// 承运商信息卡片
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('#083344')
.marginBottom(12);
// 承运商列表
ForEach(this.carriers, (carrier: Carrier) => {
Column()
.backgroundColor('#f0fdfa')
.borderRadius(8)
.padding(12)
.marginBottom(8) {
// 承运商基本信息
Row()
.justifyContent(FlexAlign.SpaceBetween)
.alignItems(ItemAlign.Center)
.marginBottom(8) {
Text(carrier.name)
.fontSize(14)
.fontWeight(FontWeight.Medium)
.fontColor('#083344');
Text(`⭐ ${carrier.rating}`)
.fontSize(12)
.fontColor('#64748b');
}
// 计价信息
Row()
.justifyContent(FlexAlign.SpaceBetween)
.marginBottom(4) {
Text(`起步价: ¥${carrier.basePrice}`)
.fontSize(12)
.fontColor('#64748b');
Text(`重量费: ¥${carrier.pricePerKg}/kg`)
.fontSize(12)
.fontColor('#64748b');
Text(`体积费: ¥${carrier.pricePerVolume}/m³`)
.fontSize(12)
.fontColor('#64748b');
}
// 服务项目
Text(`服务项目: ${carrier.services.join('、')}`)
.fontSize(12)
.fontColor('#64748b');
}
})
}
// 使用说明
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('#083344')
.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);
}
}
}
// 底部导航
Row()
.justifyContent(FlexAlign.SpaceAround)
.backgroundColor('#ffffff')
.borderTop({ width: 1, color: '#a5f3fc' })
.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: '#06b6d4' }) {
Text('👤')
.fontSize(20)
.fontColor('#06b6d4')
.marginBottom(4);
Text('我的')
.fontSize(12)
.fontColor('#06b6d4')
.fontWeight(FontWeight.Medium);
}
}
}
}
1. 原则
- 计价模型企业级复用:承运商、运输需求、报价的类型定义在两端保持一致,包含基础价、重量单价、体积单价等大宗物流计费核心属性,保证计费逻辑的专业性和准确性;
- 视觉规范100%对齐:青蓝色系主色调、报价卡片样式、状态颜色映射等视觉属性完全复用,符合大宗物流行业的视觉认知和操作习惯;
- 批量报价算法统一:按运输需求ID的精准计价、价格排序、状态更新等核心算法逻辑保持一致,保证大宗物流报价结果的准确性;
- 批量处理逻辑对等:运输需求的批量展示、独立报价、状态管理等逻辑保持一致,符合企业用户批量处理物流需求的操作习惯;
- 多维度对比体验一致:按价格、评分、服务类型的多维度对比展示逻辑保持一致,满足企业用户综合评估的核心需求;
- 语义化设计统一:大额运费格式化、最优价标注等语义化设计保持一致,提升大宗物流报价的效率;
- 平台特性兼容:弹窗展示、列表渲染等平台特有API做适配处理,保证大宗物流应用的可用性;
- 性能优化适配:鸿蒙端利用 ForEach 组件的复用机制优化批量列表渲染性能,RN 端利用数组映射实现高效渲染,均满足大批量运输需求展示的性能要求。
2. 批量运输报价系统
- 计费规则扩展:支持阶梯计价、偏远地区加价、加急费等更复杂的大宗物流计费规则,提升系统的行业适配性;
- 批量导入导出:支持Excel批量导入运输需求、导出报价结果,满足企业用户批量处理的核心需求;
- 承运商资质管理:增加承运商资质、运力、覆盖区域等信息,支持按资质筛选,符合企业物流采购的合规要求;
- 历史报价对比:保存历史报价数据,支持同比/环比分析,帮助企业优化物流成本;
- 智能推荐算法:基于运输需求特性(重量、体积、目的地)和承运商能力,智能推荐最优承运商;
- 合同价管理:支持维护企业与承运商的合同价,计算优惠金额,符合企业长期合作的定价模式;
- 物流轨迹集成:对接承运商物流轨迹API,实现从报价到履约的全流程跟踪;
- 审批流程集成:增加报价审批流程,支持多级审批,符合企业采购的内控要求;
- 多币种支持:适配跨境大宗物流,支持多币种计价和汇率转换;
- 数据可视化分析:提供物流成本分析、承运商绩效分析等可视化报表,辅助企业决策;
- 移动端适配优化:针对平板/手机等不同终端优化批量列表展示,提升操作便捷性;
- 离线报价功能:支持离线状态下的基础报价计算,联网后同步最新费率;
- 权限精细化管理:区分报价员、审批员、管理员等角色,提供精细化的权限控制;
- 电子合同签署:集成电子合同签署功能,实现报价-签约-履约的全流程数字化。
批量运输报价系统作为企业级大宗物流的核心工具,其跨端适配的关键在于计价模型的企业级专业性、批量处理的用户体验、多维度对比的精准性。这份 React Native 实现的批量运输报价组件,通过强类型领域建模、精准的批量计价算法、企业级视觉设计,构建了高效的移动端批量报价体验;而鸿蒙 ArkTS 端的适配实现,则验证了跨端开发中"逻辑复用、体验统一"的核心原则。
- 批量运输报价系统的类型设计需贴合大宗物流计费特性,基础价设定为企业级量级,重量/体积单位匹配大宗货物计量标准,保证计费逻辑的专业性;
- 批量报价算法应基于运输需求ID实现独立计价,支持多需求并行管理,符合企业批量处理的业务逻辑;
- 状态管理采用视觉化映射(状态-颜色),便于快速识别批量运输需求的处理进度,提升企业级系统的操作效率;
- 大额运费需使用
toLocaleString()格式化展示,符合企业财务数据的阅读习惯,提升数据可读性; - 跨端适配的核心是批量计价算法复用 + 平台特性适配,按需求ID的精准计价逻辑无需重写;
- 运输需求列表采用卡片式布局,支持独立报价操作,符合企业用户批量处理物流需求的操作习惯;
- 承运商对比需展示价格、评分、服务类型等多维度信息,满足企业综合评估的核心需求;
- 报价结果按价格升序排列并标注最优价,是降低企业物流成本的关键设计手段,符合成本优先的决策逻辑。
真实演示案例代码:
// App.tsx
import React, { useState } from 'react';
import { SafeAreaView, View, Text, StyleSheet, TouchableOpacity, ScrollView, Dimensions, Alert, TextInput } from 'react-native';
// Base64 图标库
const ICONS_BASE64 = {
truck: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
money: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
compare: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
calendar: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
location: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
document: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
check: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
info: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
};
const { width, height } = Dimensions.get('window');
// 承运商类型
type Carrier = {
id: string;
name: string;
rating: number;
basePrice: number;
pricePerKg: number;
pricePerVolume: number;
deliveryTime: string;
services: string[];
};
// 运输需求类型
type TransportRequest = {
id: string;
cargoType: string;
weight: number;
volume: number;
pickupLocation: string;
deliveryLocation: string;
deliveryDate: string;
status: '待报价' | '已报价' | '已确认' | '已完成';
};
// 报价类型
type Quote = {
carrierId: string;
totalPrice: number;
deliveryTime: string;
breakdown: {
baseFee: number;
weightFee: number;
volumeFee: number;
};
};
// 批量运输报价比较应用组件
const BulkTransportQuotesApp: React.FC = () => {
const [carriers] = useState<Carrier[]>([
{
id: 'c1',
name: '顺风物流',
rating: 4.8,
basePrice: 500,
pricePerKg: 3.5,
pricePerVolume: 150,
deliveryTime: '2-3天',
services: ['整车运输', '零担配送', '仓储服务']
},
{
id: 'c2',
name: '德邦物流',
rating: 4.6,
basePrice: 400,
pricePerKg: 3.0,
pricePerVolume: 120,
deliveryTime: '3-5天',
services: ['整车运输', '零担配送']
},
{
id: 'c3',
name: '天地华宇',
rating: 4.5,
basePrice: 350,
pricePerKg: 2.8,
pricePerVolume: 100,
deliveryTime: '4-7天',
services: ['整车运输', '仓储服务']
}
]);
const [transportRequests, setTransportRequests] = useState<TransportRequest[]>([
{
id: '1',
cargoType: '电子产品',
weight: 1500,
volume: 8.5,
pickupLocation: '深圳市南山区',
deliveryLocation: '上海市浦东新区',
deliveryDate: '2023-12-20',
status: '待报价'
},
{
id: '2',
cargoType: '机械设备',
weight: 3200,
volume: 15.2,
pickupLocation: '广州市白云区',
deliveryLocation: '北京市朝阳区',
deliveryDate: '2023-12-22',
status: '已报价'
}
]);
const [quotes, setQuotes] = useState<{[key: string]: Quote[]}>({});
const [selectedRequest, setSelectedRequest] = useState<string>('');
const calculateQuotes = (requestId: string) => {
const request = transportRequests.find(r => r.id === requestId);
if (!request) return;
const calculatedQuotes = carriers.map(carrier => {
const weightFee = request.weight * carrier.pricePerKg;
const volumeFee = request.volume * carrier.pricePerVolume;
const totalPrice = carrier.basePrice + weightFee + volumeFee;
return {
carrierId: carrier.id,
totalPrice: Math.round(totalPrice),
deliveryTime: carrier.deliveryTime,
breakdown: {
baseFee: carrier.basePrice,
weightFee: Math.round(weightFee),
volumeFee: Math.round(volumeFee)
}
};
});
// 按价格排序
calculatedQuotes.sort((a, b) => a.totalPrice - b.totalPrice);
setQuotes(prev => ({ ...prev, [requestId]: calculatedQuotes }));
setSelectedRequest(requestId);
};
const getStatusColor = (status: string) => {
switch (status) {
case '待报价': return '#f59e0b';
case '已报价': return '#3b82f6';
case '已确认': return '#8b5cf6';
case '已完成': return '#10b981';
default: return '#6b7280';
}
};
const handleSelectCarrier = (requestId: string, carrierId: string) => {
const request = transportRequests.find(r => r.id === requestId);
const quote = quotes[requestId]?.find(q => q.carrierId === carrierId);
const carrier = carriers.find(c => c.id === carrierId);
if (request && quote && carrier) {
Alert.alert(
'确认承运商',
`运输需求: ${request.cargoType}\n` +
`承运商: ${carrier.name}\n` +
`总费用: ¥${quote.totalPrice.toLocaleString()}\n` +
`预计时效: ${quote.deliveryTime}\n\n` +
`费用明细:\n` +
`基础费用: ¥${quote.breakdown.baseFee}\n` +
`重量费用: ¥${quote.breakdown.weightFee}\n` +
`体积费用: ¥${quote.breakdown.volumeFee}`,
[
{ text: '取消', style: 'cancel' },
{
text: '确认选择',
onPress: () => {
setTransportRequests(transportRequests.map(r =>
r.id === requestId
? { ...r, status: '已确认' }
: r
));
setSelectedRequest('');
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.requestsCard}>
<Text style={styles.sectionTitle}>运输需求</Text>
{transportRequests.map(request => (
<View key={request.id} style={styles.requestItem}>
<View style={styles.requestHeader}>
<Text style={styles.cargoType}>{request.cargoType}</Text>
<View style={[
styles.statusBadge,
{ backgroundColor: getStatusColor(request.status) }
]}>
<Text style={styles.statusText}>{request.status}</Text>
</View>
</View>
<View style={styles.requestDetails}>
<View style={styles.detailRow}>
<Text style={styles.detailLabel}>重量:</Text>
<Text style={styles.detailValue}>{request.weight}kg</Text>
</View>
<View style={styles.detailRow}>
<Text style={styles.detailLabel}>体积:</Text>
<Text style={styles.detailValue}>{request.volume}m³</Text>
</View>
<View style={styles.detailRow}>
<Text style={styles.detailLabel}>起运:</Text>
<Text style={styles.detailValue}>{request.pickupLocation}</Text>
</View>
<View style={styles.detailRow}>
<Text style={styles.detailLabel}>送达:</Text>
<Text style={styles.detailValue}>{request.deliveryLocation}</Text>
</View>
</View>
<View style={styles.requestActions}>
{request.status === '待报价' && (
<TouchableOpacity
style={styles.quoteButton}
onPress={() => calculateQuotes(request.id)}
>
<Text style={styles.quoteButtonText}>获取报价</Text>
</TouchableOpacity>
)}
<TouchableOpacity
style={styles.detailButton}
onPress={() => Alert.alert('详情', `需求ID: ${request.id}`)}
>
<Text style={styles.detailButtonText}>查看详情</Text>
</TouchableOpacity>
</View>
</View>
))}
</View>
{/* 报价比较 */}
{selectedRequest && quotes[selectedRequest] && (
<View style={styles.quotesCard}>
<View style={styles.quotesHeader}>
<Text style={styles.sectionTitle}>报价比较</Text>
<TouchableOpacity
onPress={() => setSelectedRequest('')}
>
<Text style={styles.closeButton}>✕</Text>
</TouchableOpacity>
</View>
{quotes[selectedRequest].map((quote, index) => {
const carrier = carriers.find(c => c.id === quote.carrierId);
return (
<TouchableOpacity
key={quote.carrierId}
style={styles.quoteItem}
onPress={() => handleSelectCarrier(selectedRequest, quote.carrierId)}
>
<View style={styles.quoteHeader}>
<View style={styles.carrierInfo}>
<Text style={styles.carrierName}>{carrier?.name}</Text>
<View style={styles.carrierRating}>
<Text>⭐ {carrier?.rating}</Text>
<Text style={styles.deliveryTime}>{quote.deliveryTime}</Text>
</View>
<Text style={styles.services}>
服务: {carrier?.services.join('、')}
</Text>
</View>
<View style={styles.priceSection}>
<Text style={styles.totalPrice}>¥{quote.totalPrice.toLocaleString()}</Text>
{index === 0 && (
<Text style={styles.bestPrice}>最优价</Text>
)}
</View>
</View>
<View style={styles.feeBreakdown}>
<Text style={styles.feeItem}>基础: ¥{quote.breakdown.baseFee}</Text>
<Text style={styles.feeItem}>重量: ¥{quote.breakdown.weightFee}</Text>
<Text style={styles.feeItem}>体积: ¥{quote.breakdown.volumeFee}</Text>
</View>
</TouchableOpacity>
);
})}
</View>
)}
{/* 承运商信息 */}
<View style={styles.carriersCard}>
<Text style={styles.sectionTitle}>合作承运商</Text>
{carriers.map(carrier => (
<View key={carrier.id} style={styles.carrierItem}>
<View style={styles.carrierBasicInfo}>
<Text style={styles.carrierName}>{carrier.name}</Text>
<View style={styles.carrierRating}>
<Text>⭐ {carrier.rating}</Text>
</View>
</View>
<View style={styles.carrierPricing}>
<Text style={styles.pricingText}>起步价: ¥{carrier.basePrice}</Text>
<Text style={styles.pricingText}>重量费: ¥{carrier.pricePerKg}/kg</Text>
<Text style={styles.pricingText}>体积费: ¥{carrier.pricePerVolume}/m³</Text>
</View>
<Text style={styles.servicesText}>
服务项目: {carrier.services.join('、')}
</Text>
</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: '#ecfeff',
},
header: {
flexDirection: 'column',
padding: 16,
backgroundColor: '#ffffff',
borderBottomWidth: 1,
borderBottomColor: '#a5f3fc',
},
title: {
fontSize: 20,
fontWeight: 'bold',
color: '#083344',
marginBottom: 4,
},
subtitle: {
fontSize: 14,
color: '#06b6d4',
},
content: {
flex: 1,
marginTop: 12,
},
requestsCard: {
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: '#083344',
marginBottom: 12,
},
requestItem: {
padding: 12,
borderBottomWidth: 1,
borderBottomColor: '#a5f3fc',
},
requestHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 8,
},
cargoType: {
fontSize: 16,
fontWeight: '600',
color: '#083344',
},
statusBadge: {
paddingHorizontal: 8,
paddingVertical: 4,
borderRadius: 12,
},
statusText: {
fontSize: 12,
color: '#ffffff',
fontWeight: '500',
},
requestDetails: {
marginBottom: 12,
},
detailRow: {
flexDirection: 'row',
marginBottom: 4,
},
detailLabel: {
fontSize: 12,
color: '#64748b',
width: 50,
},
detailValue: {
fontSize: 12,
color: '#083344',
flex: 1,
},
requestActions: {
flexDirection: 'row',
justifyContent: 'flex-end',
},
quoteButton: {
backgroundColor: '#06b6d4',
paddingHorizontal: 12,
paddingVertical: 6,
borderRadius: 16,
marginRight: 8,
},
quoteButtonText: {
color: '#ffffff',
fontSize: 12,
},
detailButton: {
backgroundColor: '#cffafe',
paddingHorizontal: 12,
paddingVertical: 6,
borderRadius: 16,
},
detailButtonText: {
color: '#06b6d4',
fontSize: 12,
},
quotesCard: {
backgroundColor: '#ffffff',
marginHorizontal: 16,
marginBottom: 12,
borderRadius: 12,
padding: 16,
elevation: 2,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
},
quotesHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 12,
},
closeButton: {
fontSize: 18,
color: '#64748b',
},
quoteItem: {
backgroundColor: '#f0fdfa',
borderRadius: 8,
padding: 12,
marginBottom: 8,
},
quoteHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 8,
},
carrierInfo: {
flex: 1,
},
carrierName: {
fontSize: 14,
fontWeight: '500',
color: '#083344',
marginBottom: 2,
},
carrierRating: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 4,
},
deliveryTime: {
fontSize: 12,
color: '#64748b',
marginLeft: 8,
},
services: {
fontSize: 12,
color: '#64748b',
},
priceSection: {
alignItems: 'flex-end',
},
totalPrice: {
fontSize: 18,
fontWeight: 'bold',
color: '#06b6d4',
},
bestPrice: {
fontSize: 12,
color: '#10b981',
backgroundColor: '#d1fae5',
paddingHorizontal: 6,
paddingVertical: 2,
borderRadius: 10,
marginTop: 2,
},
feeBreakdown: {
flexDirection: 'row',
justifyContent: 'space-between',
},
feeItem: {
fontSize: 12,
color: '#64748b',
},
carriersCard: {
backgroundColor: '#ffffff',
marginHorizontal: 16,
marginBottom: 12,
borderRadius: 12,
padding: 16,
elevation: 2,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
},
carrierItem: {
backgroundColor: '#f0fdfa',
borderRadius: 8,
padding: 12,
marginBottom: 8,
},
carrierBasicInfo: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 8,
},
carrierPricing: {
flexDirection: 'row',
justifyContent: 'space-between',
marginBottom: 4,
},
pricingText: {
fontSize: 12,
color: '#64748b',
},
servicesText: {
fontSize: 12,
color: '#64748b',
},
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: '#a5f3fc',
paddingVertical: 12,
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
},
navItem: {
alignItems: 'center',
flex: 1,
},
activeNavItem: {
paddingTop: 4,
borderTopWidth: 2,
borderTopColor: '#06b6d4',
},
navIcon: {
fontSize: 20,
color: '#94a3b8',
marginBottom: 4,
},
activeNavIcon: {
color: '#06b6d4',
},
navText: {
fontSize: 12,
color: '#94a3b8',
},
activeNavText: {
color: '#06b6d4',
fontWeight: '500',
},
});
export default BulkTransportQuotesApp;

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

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

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

本文介绍了基于React Native开发的批量运输报价应用,采用函数式组件架构实现跨平台支持。核心功能包括运输需求管理、智能报价计算和承运商比较,使用TypeScript确保类型安全。文章详细解析了承运商、运输需求和报价的数据结构设计,以及状态管理和业务逻辑的实现。特别针对鸿蒙系统进行了兼容性优化,包括组件适配和性能调优。最后提出了类型增强、状态持久化和组件化等改进方向,为构建高效的物流管理系统提供了技术参考。
欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。
更多推荐
所有评论(0)