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


架构

本项目采用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:体积信息,对运输空间占用至关重要
  • pickupLocationdeliveryLocation:起运地和目的地,影响运输路线
  • 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:选中的运输需求,用于显示报价信息

状态更新机制

  1. 报价计算:通过calculateQuotes函数,根据运输需求计算各承运商报价
  2. 状态更新:通过setTransportRequests函数,更新运输需求状态
  3. 报价存储:通过setQuotes函数,将计算结果存储到对应需求ID下
  4. 选择管理:通过setSelectedRequest函数,管理当前选中的需求

状态管理

  • 不可变数据模式:使用扩展运算符(...)创建新状态,避免直接修改原状态
  • 对象存储优化:使用对象存储报价信息,通过需求ID快速查找
  • 批量更新:在状态更新时使用map方法批量处理,确保数据一致性
  • 状态清理:在确认承运商后,清理选中状态,保持界面整洁

核心业务

  1. 运输需求管理:展示和管理多个运输需求
  2. 报价计算:根据需求信息计算各承运商报价
  3. 价格比较:展示各承运商报价,支持价格排序
  4. 承运商选择:用户选择合适的承运商
  5. 状态更新:更新运输需求状态为"已确认"

核心计算

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图标:在鸿蒙系统中同样支持,可减少网络请求,提升性能
  • 内存管理:鸿蒙系统对内存使用更为严格,需注意资源释放
  • 计算性能:对于价格计算等数学运算,鸿蒙系统的处理性能与其他平台相当
  1. 渲染性能

    • 避免不必要的重渲染,合理使用React.memo
    • 对于长列表,建议使用FlatList替代ScrollView
    • 优化组件结构,减少嵌套层级
  2. 计算性能

    • 缓存计算结果,避免重复计算
    • 优化数学运算,减少不必要的计算步骤
    • 考虑使用useMemo缓存计算结果,特别是对于复杂的报价计算
  3. 内存管理

    • 及时释放不再使用的资源
    • 避免内存泄漏,特别是在处理多个运输需求时
    • 合理使用缓存策略,平衡性能和内存占用

平台适配

  • 条件渲染:使用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,减少应用体积

  1. 批量运输管理:实现了多个运输需求的同时管理,提高物流效率
  2. 智能报价计算:根据重量和体积自动计算各承运商报价,支持价格排序
  3. 详细费用明细:提供基础费用、重量费用和体积费用的详细 breakdown,提升透明度
  4. 状态追踪:实时更新运输需求状态,确保业务流程透明
  5. 类型安全:充分利用TypeScript的类型系统,提高代码质量和开发效率
  6. 响应式设计:使用Flexbox和Dimensions API,实现适配不同屏幕尺寸的布局
  7. 内置图标资源:使用Base64编码图标,减少网络依赖,提升加载速度
  8. 用户友好的交互:提供清晰的操作反馈和信息提示,提升用户体验
  9. 模块化架构:代码结构模块化,便于维护和扩展

本项目展示了如何使用React Native和TypeScript构建一个功能完整的批量运输报价比较应用。通过合理的架构设计、类型定义和状态管理,实现了跨平台的一致性体验。


随着React Native和鸿蒙系统的不断发展,跨端开发将会变得更加成熟和高效。未来可以考虑:

  1. 使用React Native 0.70+版本:利用新的架构特性,如Fabric渲染器和Turbo Modules,提升应用性能
  2. 探索鸿蒙原生能力:充分利用鸿蒙系统的分布式能力,实现多设备协同和更丰富的功能
  3. 引入现代化状态管理:使用Redux Toolkit或Zustand等现代状态管理库,简化状态管理
  4. 实现PWA支持:扩展应用的使用场景,支持Web平台
  5. 集成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}` },
                    { 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 端的适配实现,则验证了跨端开发中"逻辑复用、体验统一"的核心原则。

  1. 批量运输报价系统的类型设计需贴合大宗物流计费特性,基础价设定为企业级量级,重量/体积单位匹配大宗货物计量标准,保证计费逻辑的专业性;
  2. 批量报价算法应基于运输需求ID实现独立计价,支持多需求并行管理,符合企业批量处理的业务逻辑;
  3. 状态管理采用视觉化映射(状态-颜色),便于快速识别批量运输需求的处理进度,提升企业级系统的操作效率;
  4. 大额运费需使用 toLocaleString() 格式化展示,符合企业财务数据的阅读习惯,提升数据可读性;
  5. 跨端适配的核心是批量计价算法复用 + 平台特性适配,按需求ID的精准计价逻辑无需重写;
  6. 运输需求列表采用卡片式布局,支持独立报价操作,符合企业用户批量处理物流需求的操作习惯;
  7. 承运商对比需展示价格、评分、服务类型等多维度信息,满足企业综合评估的核心需求;
  8. 报价结果按价格升序排列并标注最优价,是降低企业物流成本的关键设计手段,符合成本优先的决策逻辑。

真实演示案例代码:

// 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}</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}/</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确保类型安全。文章详细解析了承运商、运输需求和报价的数据结构设计,以及状态管理和业务逻辑的实现。特别针对鸿蒙系统进行了兼容性优化,包括组件适配和性能调优。最后提出了类型增强、状态持久化和组件化等改进方向,为构建高效的物流管理系统提供了技术参考。

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

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐