运输管理系统(TMS)| 司机App开发:React Native完成运输任务签收

作者:袖梨 2026-07-06

运输管理系统(TMS)| 司机App开发:React Native实现运输任务签收

{"type":"doc","content":[{"type":"heading","attrs":{"id":"8fceaac6-2c4d-4f77-94db-17355a10e92a","textAlign":"inherit","indent":0,"level":1,"isHoverDragHandle":false},"content":[{"type":"text","text":"引言 "}]},{"type":"paragraph","attrs":{"id":"9d997cc1-4f6b-4e00-b717-8dfbc2f4383d","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"在TMS的闭环链条中,司机App是连接线下物理运输与线上数字系统的“神经末梢”。当车辆抵达目的地并完成卸货后,"},{"type":"text","marks":[{"type":"bold"}],"text":"签收环节"},{"type":"text","text":"是整个运输流程从“在途”转向“结算”的关键闸门。传统的纸质回单存在易丢失、易伪造、流转慢、归档难等痛点,往往导致财务结算滞后。"}]},{"type":"paragraph","attrs":{"id":"f456c0aa-1c9b-4f72-b02f-27b70b3b05c6","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"本文将基于React Native,详细拆解如何打造一个高性能、高可靠性的运输任务签收模块。我们将重点攻克"},{"type":"text","marks":[{"type":"bold"}],"text":"电子签名手写板"},{"type":"text","text":"、"},{"type":"text","marks":[{"type":"bold"}],"text":"大图拍摄与压缩上传"},{"type":"text","text":"、"},{"type":"text","marks":[{"type":"bold"}],"text":"离线缓存"},{"type":"text","text":"三大核心技术难点,确保即使在隧道、偏远山区等弱网或无网环境下,司机也能顺利完成签收,实现回单的数字化闭环管理。"}]},{"type":"heading","attrs":{"id":"abf8fbe6-4a7c-4950-87f2-896cecd3dae6","textAlign":"inherit","indent":0,"level":1,"isHoverDragHandle":false},"content":[{"type":"text","text":"一、技术选型与架构设计 "}]},{"type":"paragraph","attrs":{"id":"eee03397-2f29-42c4-8b60-a80714619f99","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"为什么选择React Native?在物流行业,司机使用的终端设备碎片化严重(安卓大屏机、手持PDA、个人手机),RN的“一次编写,多端运行”特性能够极大降低维护成本。同时,其接近原生的性能足以支撑复杂的画板和相机操作。"}]},{"type":"heading","attrs":{"id":"429faee3-f32e-4dd0-8582-29d468cab19e","textAlign":"inherit","indent":0,"level":2,"isHoverDragHandle":false},"content":[{"type":"text","text":"1.1 核心技术栈 "}]},{"type":"table","attrs":{"id":"8f9d91fc-f614-49a4-8327-2fbb7f2c4d40","isHoverDragHandle":false},"content":[{"type":"tableRow","content":[{"type":"tableCell","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"background":null,"color":null,"textAlign":null,"style":"border-color: rgb(217, 217, 217);"},"content":[{"type":"paragraph","attrs":{"id":"e2b18b56-0c01-4cfa-bf40-17721ec00a45","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"textStyle","attrs":{"color":"","background":""}}],"text":"功能模块"}]}]},{"type":"tableCell","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"background":null,"color":null,"textAlign":null,"style":"border-color: rgb(217, 217, 217);"},"content":[{"type":"paragraph","attrs":{"id":"57cab7f6-b2e4-4753-8083-b895f74f9712","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"textStyle","attrs":{"color":"","background":""}}],"text":"技术/库"}]}]},{"type":"tableCell","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"background":null,"color":null,"textAlign":null,"style":"border-color: rgb(217, 217, 217);"},"content":[{"type":"paragraph","attrs":{"id":"81759a32-0d24-490c-a34a-cb1652196097","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"textStyle","attrs":{"color":"","background":""}}],"text":"选型理由"}]}]}]},{"type":"tableRow","content":[{"type":"tableCell","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"background":null,"color":null,"textAlign":null,"style":"border-color: rgb(217, 217, 217);"},"content":[{"type":"paragraph","attrs":{"id":"b19c224f-b175-4a91-91c5-6772c017b07b","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"textStyle","attrs":{"color":"","background":""}},{"type":"bold"}],"text":"手写签名"}]}]},{"type":"tableCell","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"background":null,"color":null,"textAlign":null,"style":"border-color: rgb(217, 217, 217);"},"content":[{"type":"paragraph","attrs":{"id":"8b329eeb-082a-40c8-aca0-8019751e4d77","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"code"}],"text":"react-native-signature-canvas"}]}]},{"type":"tableCell","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"background":null,"color":null,"textAlign":null,"style":"border-color: rgb(217, 217, 217);"},"content":[{"type":"paragraph","attrs":{"id":"54b39c1f-23fa-46be-b23d-2f60b55876ce","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"textStyle","attrs":{"color":"","background":""}}],"text":"基于原生WebView封装,性能优于纯JS实现,支持压感和笔锋"}]}]}]},{"type":"tableRow","content":[{"type":"tableCell","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"background":null,"color":null,"textAlign":null,"style":"border-color: rgb(217, 217, 217);"},"content":[{"type":"paragraph","attrs":{"id":"71f0152f-514e-4869-b989-10e8783d3dd8","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"textStyle","attrs":{"color":"","background":""}},{"type":"bold"}],"text":"图像采集"}]}]},{"type":"tableCell","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"background":null,"color":null,"textAlign":null,"style":"border-color: rgb(217, 217, 217);"},"content":[{"type":"paragraph","attrs":{"id":"25f49b7e-3029-4223-a60c-8d18b178403f","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"code"}],"text":"react-native-image-picker"},{"type":"text","marks":[{"type":"textStyle","attrs":{"color":"","background":""}}],"text":" "},{"type":"text","marks":[{"type":"code"}],"text":"react-native-image-resizer"}]}]},{"type":"tableCell","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"background":null,"color":null,"textAlign":null,"style":"border-color: rgb(217, 217, 217);"},"content":[{"type":"paragraph","attrs":{"id":"1ff6c9a9-66af-4b42-85ac-44f22c5f85e2","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"textStyle","attrs":{"color":"","background":""}}],"text":"前者调用系统相机/相册,后者解决大图上传慢的问题"}]}]}]},{"type":"tableRow","content":[{"type":"tableCell","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"background":null,"color":null,"textAlign":null,"style":"border-color: rgb(217, 217, 217);"},"content":[{"type":"paragraph","attrs":{"id":"fca87c8e-8414-4fd1-96eb-595e46ea1238","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"textStyle","attrs":{"color":"","background":""}},{"type":"bold"}],"text":"网络请求"}]}]},{"type":"tableCell","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"background":null,"color":null,"textAlign":null,"style":"border-color: rgb(217, 217, 217);"},"content":[{"type":"paragraph","attrs":{"id":"097cb6bf-ad1d-44e7-b810-d2acc672be07","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"code"}],"text":"axios"},{"type":"text","marks":[{"type":"textStyle","attrs":{"color":"","background":""}}],"text":" "},{"type":"text","marks":[{"type":"code"}],"text":"rn-fetch-blob"}]}]},{"type":"tableCell","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"background":null,"color":null,"textAlign":null,"style":"border-color: rgb(217, 217, 217);"},"content":[{"type":"paragraph","attrs":{"id":"9bec677b-83da-4ca4-baae-64bfe8925f68","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"code"}],"text":"rn-fetch-blob"},{"type":"text","marks":[{"type":"textStyle","attrs":{"color":"","background":""}}],"text":"支持文件断点续传,适合不稳定网络"}]}]}]},{"type":"tableRow","content":[{"type":"tableCell","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"background":null,"color":null,"textAlign":null,"style":"border-color: rgb(217, 217, 217);"},"content":[{"type":"paragraph","attrs":{"id":"a32d1199-a2fd-4bd3-9757-bf842b798adc","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"textStyle","attrs":{"color":"","background":""}},{"type":"bold"}],"text":"本地存储"}]}]},{"type":"tableCell","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"background":null,"color":null,"textAlign":null,"style":"border-color: rgb(217, 217, 217);"},"content":[{"type":"paragraph","attrs":{"id":"fefdd391-d32c-4d46-be90-ed52f0c9b673","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"code"}],"text":"async-storage"},{"type":"text","marks":[{"type":"textStyle","attrs":{"color":"","background":""}}],"text":" "},{"type":"text","marks":[{"type":"code"}],"text":"realm"}]}]},{"type":"tableCell","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"background":null,"color":null,"textAlign":null,"style":"border-color: rgb(217, 217, 217);"},"content":[{"type":"paragraph","attrs":{"id":"70e1d751-a79e-4acd-9ff6-0cbd87a0ae5e","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"code"}],"text":"async-storage"},{"type":"text","marks":[{"type":"textStyle","attrs":{"color":"","background":""}}],"text":"存简单配置,"},{"type":"text","marks":[{"type":"code"}],"text":"realm"},{"type":"text","marks":[{"type":"textStyle","attrs":{"color":"","background":""}}],"text":"存结构化离线数据"}]}]}]},{"type":"tableRow","content":[{"type":"tableCell","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"background":null,"color":null,"textAlign":null,"style":"border-color: rgb(217, 217, 217);"},"content":[{"type":"paragraph","attrs":{"id":"6a30ded1-ac46-4ce9-92db-1f0e53691e27","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"textStyle","attrs":{"color":"","background":""}},{"type":"bold"}],"text":"UI组件"}]}]},{"type":"tableCell","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"background":null,"color":null,"textAlign":null,"style":"border-color: rgb(217, 217, 217);"},"content":[{"type":"paragraph","attrs":{"id":"80a26a0b-8593-4a31-91f8-12ab08e3fb1c","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"code"}],"text":"react-native-paper"}]}]},{"type":"tableCell","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"background":null,"color":null,"textAlign":null,"style":"border-color: rgb(217, 217, 217);"},"content":[{"type":"paragraph","attrs":{"id":"ed4f930c-f846-43d5-b545-8653420c1fc3","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"textStyle","attrs":{"color":"","background":""}}],"text":"Material Design风格,开发效率高"}]}]}]}]},{"type":"heading","attrs":{"id":"6e7727a0-dd56-4330-9a84-068d9f4a8cff","textAlign":"inherit","indent":0,"level":2,"isHoverDragHandle":false},"content":[{"type":"text","text":"1.2 签收模块架构图 "}]},{"type":"image","attrs":{"id":"af06ecfe-5608-4450-905b-773b697e9df7","src":"https://developer.qcloudimg.com/http-save/audit-10133498/8cadcb115bb6260a66a396d08ae29840.png","extension":"png","align":"center","alt":"","showAlt":false,"href":"","boxShadow":"","width":1012,"aspectRatio":"1.981618","status":"success","showText":true,"isPercentage":false,"percentage":0,"isHoverDragHandle":false}},{"type":"paragraph","attrs":{"id":"d8d219d9-42c8-47fa-aef2-8f998f2560d9","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"bold"}],"text":"架构解析"},{"type":"text","text":":"}]},{"type":"bulletList","attrs":{"id":"2ec93c19-b817-488e-9c76-e3eddffbf9a5","isHoverDragHandle":false},"content":[{"type":"listItem","attrs":{"id":"40bed1e8-a3b1-4c7b-b2a5-bc9ede0530e7"},"content":[{"type":"paragraph","attrs":{"id":"ec3d85b8-725b-42de-a9b8-679a7b6f7eff","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"bold"}],"text":"离线优先(Offline First)"},{"type":"text","text":":核心设计理念。无论网络状况如何,签收操作必须成功。数据先落本地数据库(Realm),待网络恢复后由后台服务自动同步。"}]}]},{"type":"listItem","attrs":{"id":"27dab542-d8be-40d5-9403-cf5b2b34b20c"},"content":[{"type":"paragraph","attrs":{"id":"e9d037f0-5a15-44da-a078-935cf2b70871","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"bold"}],"text":"流程控制"},{"type":"text","text":":"},{"type":"text","marks":[{"type":"code"}],"text":"SignFlow Controller"},{"type":"text","text":"统一管理状态(如“已拍照但未签名”、“已签名但未上传”),防止脏数据产生。"}]}]},{"type":"listItem","attrs":{"id":"a6583e14-4bb5-4f0f-af38-7a228d09288b"},"content":[{"type":"paragraph","attrs":{"id":"d3773938-336c-41a0-97db-0e18d9341bdb","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"bold"}],"text":"原生桥接"},{"type":"text","text":":复杂的图像处理和网络请求通过原生模块执行,避免JS线程阻塞导致UI卡顿。"}]}]}]},{"type":"heading","attrs":{"id":"a12ae0b9-23ea-4b86-a638-14957d962d83","textAlign":"inherit","indent":0,"level":1,"isHoverDragHandle":false},"content":[{"type":"text","text":"二、核心功能实现:电子签名板 "}]},{"type":"paragraph","attrs":{"id":"3063c963-3b3a-4525-a481-209c000aa25f","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"电子签名是法律效力的核心。我们需要一个流畅、清晰且能导出高清图片的手写板。"}]},{"type":"heading","attrs":{"id":"1b35283b-fd88-4a2e-8e6f-35546d0dd52d","textAlign":"inherit","indent":0,"level":2,"isHoverDragHandle":false},"content":[{"type":"text","text":"2.1 签名板组件封装 "}]},{"type":"codeBlock","attrs":{"id":"a6e14ed9-f1fd-4325-813c-f1f6a9d51c5f","language":"javascript","theme":"atom-one-dark","runtimes":0,"isHoverDragHandle":false,"key":"","languageByAi":"javascript"},"content":[{"type":"text","text":"import React, { useRef, useState } from 'react';nimport { View, Button, StyleSheet, Alert } from 'react-native';nimport SignatureCanvas from 'react-native-signature-canvas';nnconst SignaturePad = ({ onOK, onEmpty }) => {nconst ref = useRef();nconst [signatureBase64, setSignatureBase64] = useState(null);nn// 当绘制完成时触发(手指离开屏幕)nconst handleEnd = () => {nref.current.readSignature();n};nn// 获取到Base64数据nconst handleData = (base64Data) => {nsetSignatureBase64(base64Data);n// Base64格式: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..."n};nnconst handleClear = () => {nref.current.clearSignature();nsetSignatureBase64(null);nonEmpty && onEmpty();n};nnconst handleConfirm = () => {nif (!signatureBase64) {nAlert.alert('提示', '请先进行手写签名');nreturn;n}nonOK && onOK(signatureBase64);n};nnconst style = `.m-signature-pad { box-shadow: none; border: 1px solid #ddd; } n .m-signature-pad--body { border: none; } n .m-signature-pad--footer { display: none; }`;nnreturn (nnnnnnnn);n};nnconst styles = StyleSheet.create({ncontainer: { flex: 1, margin: 16 },ncanvas: { flex: 1, borderWidth: 1, borderColor: '#ccc' },nbuttonContainer: { flexDirection: 'row', justifyContent: 'space-around', marginTop: 16 }n});nnexport default SignaturePad;"}]},{"type":"paragraph","attrs":{"id":"911a64fb-3a47-465e-9419-c91b32513c03","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"bold"}],"text":"代码深度解析"},{"type":"text","text":":"}]},{"type":"bulletList","attrs":{"id":"ec24ba0b-e72a-455b-b0ae-506f6ed037f9","isHoverDragHandle":false},"content":[{"type":"listItem","attrs":{"id":"954c068c-502b-4b8b-92b7-f64d437c2e57"},"content":[{"type":"paragraph","attrs":{"id":"fc73a9e5-b756-4839-ac26-9cb61b46287e","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"bold"}],"text":"Ref控制"},{"type":"text","text":":通过"},{"type":"text","marks":[{"type":"code"}],"text":"ref"},{"type":"text","text":"直接调用组件内部的"},{"type":"text","marks":[{"type":"code"}],"text":"readSignature"},{"type":"text","text":"和"},{"type":"text","marks":[{"type":"code"}],"text":"clearSignature"},{"type":"text","text":"方法,实现精准控制。"}]}]},{"type":"listItem","attrs":{"id":"d410aa70-d988-4639-8a43-518bad85d0ce"},"content":[{"type":"paragraph","attrs":{"id":"0caa1eda-98d9-4c96-92f7-0ee740a32264","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"bold"}],"text":"Base64传输"},{"type":"text","text":":签名板直接输出Base64编码的图片数据。这非常适合直接传给后端或存入本地数据库,避免了文件路径管理的复杂性。"}]}]},{"type":"listItem","attrs":{"id":"49a8b8f2-d196-48f7-b659-ee2775ca10a0"},"content":[{"type":"paragraph","attrs":{"id":"3e5f1f39-dee5-45ae-88da-fab3ab7b2071","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"bold"}],"text":"WebStyle属性"},{"type":"text","text":":由于该库底层基于WebView,我们可以通过注入CSS("},{"type":"text","marks":[{"type":"code"}],"text":"webStyle"},{"type":"text","text":")来深度定制外观,比如去掉默认的Footer按钮,使用我们自己的React Native按钮,保持App整体UI风格一致。"}]}]},{"type":"listItem","attrs":{"id":"34639872-d0d4-4f42-a879-46468c9916a4"},"content":[{"type":"paragraph","attrs":{"id":"164c82f8-663c-455a-929d-16edac3833aa","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"bold"}],"text":"性能注意"},{"type":"text","text":":Base64数据体积较大(比二进制大30%),不适合一次性加载过多张,适合单张签名图的即时处理。"}]}]}]},{"type":"heading","attrs":{"id":"968f7b95-12d3-462e-8816-fc21b1239574","textAlign":"inherit","indent":0,"level":1,"isHoverDragHandle":false},"content":[{"type":"text","text":"三、核心功能实现:照片上传与压缩 "}]},{"type":"paragraph","attrs":{"id":"239e89e3-01f0-4348-b8bb-b26259e240f3","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"货物照片(特别是异常破损照片)是理赔的重要依据。手机摄像头动辄1200万像素,一张照片可能高达5MB,直接上传在4G网络下都是灾难。必须进行"},{"type":"text","marks":[{"type":"bold"}],"text":"压缩"},{"type":"text","text":"。"}]},{"type":"heading","attrs":{"id":"413e997e-36b0-4ca3-a24d-1597c5279c06","textAlign":"inherit","indent":0,"level":2,"isHoverDragHandle":false},"content":[{"type":"text","text":"3.1 图像选择与压缩流程 "}]},{"type":"codeBlock","attrs":{"id":"4d5f0059-b7d2-4014-a75a-4ac9e4a9ae68","language":"javascript","theme":"atom-one-dark","runtimes":0,"isHoverDragHandle":false,"key":"","languageByAi":"javascript"},"content":[{"type":"text","text":"import ImagePicker from 'react-native-image-picker';nimport ImageResizer from 'react-native-image-resizer';nimport RNFS from 'react-native-fs';nnconst imageService = {n// 1. 打开相机nlaunchCamera: async () => {nconst options = {nmediaType: 'photo',nquality: 1.0, // 先获取原图,后续压缩nmaxWidth: 2000, // 限制最大宽度nmaxHeight: 2000, // 限制最大高度nincludeBase64: false, // 暂时不包含base64,节省内存nsaveToPhotos: false // 不保存到系统相册,保护隐私n};nnreturn new Promise((resolve, reject) => {nImagePicker.launchCamera(options, (response) => {nif (response.didCancel) {nresolve(null);n} else if (response.errorCode) {nreject(new Error(response.errorMessage));n} else {nresolve(response.assets[0]);n}n});n});n},nn// 2. 压缩图片ncompressImage: async (uri, fileName) => {ntry {nconst compressedImage = await ImageResizer.createResizedImage(nuri,n1280, // 宽度:1280px足够清晰,且体积适中n960,// 高度n'JPEG', // 格式n80,// 质量:80%肉眼无差别,体积减半n0, // 旋转角度nRNFS.CachesDirectoryPath // 存储路径n);n// compressedImage.uri: file:///.../.cache/resize.jpgnreturn compressedImage;n} catch (error) {nconsole.error('压缩失败:', error);nthrow error;n}n},nn// 3. 转换为Base64(用于上传或存DB)nconvertToBase64: async (uri) => {ntry {nconst base64 = await RNFS.readFile(uri, 'base64');nreturn `data:image/jpeg;base64,${base64}`;n} catch (error) {nconsole.error('Base64转换失败:', error);nreturn null;n}n}n};nnexport default imageService;"}]},{"type":"paragraph","attrs":{"id":"5d8b14f2-8533-4000-93f8-048478c8a2e9","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"bold"}],"text":"代码深度解析"},{"type":"text","text":":"}]},{"type":"bulletList","attrs":{"id":"1401ac77-6d21-405c-a4b8-e5463d5f9ee9","isHoverDragHandle":false},"content":[{"type":"listItem","attrs":{"id":"51f7fc09-2ee8-406c-940e-168de620028d"},"content":[{"type":"paragraph","attrs":{"id":"d8b66254-7f84-4d16-834f-fd88b60d4d1b","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"bold"}],"text":"两步法压缩"},{"type":"text","text":":先通过"},{"type":"text","marks":[{"type":"code"}],"text":"ImagePicker"},{"type":"text","text":"限制硬件采集的最大尺寸("},{"type":"text","marks":[{"type":"code"}],"text":"maxWidth/maxHeight"},{"type":"text","text":"),再通过"},{"type":"text","marks":[{"type":"code"}],"text":"ImageResizer"},{"type":"text","text":"进行二次压缩。这是因为部分手机硬件采集的参数优先级高于Picker的设置。"}]}]},{"type":"listItem","attrs":{"id":"dec72770-2896-4168-b343-d4e51edf302d"},"content":[{"type":"paragraph","attrs":{"id":"b6e4d820-4199-40f0-b536-c81ee5152b44","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"bold"}],"text":"黄金参数"},{"type":"text","text":":"},{"type":"text","marks":[{"type":"code"}],"text":"1280x960"},{"type":"text","text":" "},{"type":"text","marks":[{"type":"code"}],"text":"Quality 80"},{"type":"text","text":"是经过验证的“甜点”参数。一张原图5MB的照片,压缩后通常在200KB-400KB之间,清晰度足以辨认货物细节,上传速度提升10倍以上。"}]}]},{"type":"listItem","attrs":{"id":"aa0cf427-3934-4be0-b5e7-37bb9819055d"},"content":[{"type":"paragraph","attrs":{"id":"3e786a3b-31a7-46a0-aeef-5b0f112865db","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"bold"}],"text":"缓存目录"},{"type":"text","text":":压缩后的图片存放在"},{"type":"text","marks":[{"type":"code"}],"text":"RNFS.CachesDirectoryPath"},{"type":"text","text":"(应用缓存目录),而非Documents目录,方便系统清理,避免占用用户过多存储空间。"}]}]}]},{"type":"heading","attrs":{"id":"aaaecbcf-11d6-4fc4-a204-b82d1427d798","textAlign":"inherit","indent":0,"level":1,"isHoverDragHandle":false},"content":[{"type":"text","text":"四、离线签收与队列同步 "}]},{"type":"paragraph","attrs":{"id":"2dc83226-872d-4859-914a-3117f5f20883","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"这是本篇最难也是最有价值的部分。司机进入地下车库或偏远地区时,网络中断怎么办?我们必须实现一个"},{"type":"text","marks":[{"type":"bold"}],"text":"可靠的离线队列"},{"type":"text","text":"。"}]},{"type":"heading","attrs":{"id":"02a8840d-6efa-415a-9a42-76f999424320","textAlign":"inherit","indent":0,"level":2,"isHoverDragHandle":false},"content":[{"type":"text","text":"4.1 使用Realm存储离线数据 "}]},{"type":"paragraph","attrs":{"id":"031ee48e-4b83-477c-8b2c-e56de732f2fd","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"相比AsyncStorage,Realm更适合存储结构化的、需要查询的数据。"}]},{"type":"codeBlock","attrs":{"id":"880f2184-df38-47ae-b2df-4d8524e5edd6","language":"javascript","theme":"atom-one-dark","runtimes":0,"isHoverDragHandle":false,"key":"","languageByAi":"javascript"},"content":[{"type":"text","text":"export const SignRecordSchema = {nname: 'SignRecord',nprimaryKey: 'localId',nproperties: {nlocalId: 'string', // UUID,本地唯一标识ntaskId: 'string', // 任务IDnsignatureBase64: 'string?', // 签名数据nphotos: 'string[]', // 照片Base64数组nlatitude: 'double?', // 签收纬度nlongitude: 'double?', // 签收经度nsignedAt: 'date', // 签收时间nstatus: 'string', // PENDING, UPLOADING, SUCCESS, FAILEDnretryCount: 'int', // 重试次数nerrorMessage: 'string?' // 错误信息n}n};nn// services/offlineQueue.jsnimport Realm from 'realm';nimport { SignRecordSchema } from '../database/schemas';nimport NetInfo from '@react-native-community/netinfo';nnclass OfflineQueue {nconstructor() {nthis.realm = null;nthis.init();nthis.setupNetworkListener();n}nnasync init() {nthis.realm = await Realm.open({nschema: [SignRecordSchema],nschemaVersion: 1n});n}nn// 添加记录到队列nadd(record) {nif (!this.realm) return;nthis.realm.write(() => {nthis.realm.create('SignRecord', {n...record,nstatus: 'PENDING',nretryCount: 0n});n});n}nn// 监听网络变化nsetupNetworkListener() {nNetInfo.addEventListener(state => {nif (state.isConnected && state.isInternetReachable) {nconsole.log('网络恢复,开始同步...');nthis.syncPendingRecords();n}n});n}nn// 同步逻辑nasync syncPendingRecords() {nif (!this.realm) return;nconst pendingRecords = this.realm.objects('SignRecord').filtered('status == "PENDING" OR (status == "FAILED" AND retryCount < 3)');nnfor (const record of pendingRecords) {ntry {nthis.realm.write(() => {nrecord.status = 'UPLOADING';n});nn// 调用上传API (此处为伪代码)nawait this.uploadToServer(record);nnthis.realm.write(() => {nrecord.status = 'SUCCESS';n});nconsole.log(`记录 ${record.localId} 上传成功`);n} catch (error) {nthis.realm.write(() => {nrecord.status = 'FAILED';nrecord.retryCount = 1;nrecord.errorMessage = error.message;n});nconsole.warn(`记录 ${record.localId} 上传失败,重试次数 ${record.retryCount}`);n}n}n}nn// 伪代码:上传到服务器nuploadToServer(record) {n// 使用 FormData 或 GraphQL Multipart Requestn// 注意:上传Base64时,后端可能需要解码为二进制文件nreturn new Promise((resolve) => setTimeout(resolve, 1000));n}n}nnexport default new OfflineQueue();"}]},{"type":"paragraph","attrs":{"id":"401ad595-e1fa-426b-9a7b-720679a82c66","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"bold"}],"text":"代码深度解析"},{"type":"text","text":":"}]},{"type":"bulletList","attrs":{"id":"929948ad-6c75-47e9-9cb5-76f0e1667d41","isHoverDragHandle":false},"content":[{"type":"listItem","attrs":{"id":"76548212-96b2-4a2c-a1c2-e45d8e71188e"},"content":[{"type":"paragraph","attrs":{"id":"ec8aa1a7-7d8d-4f99-9d7a-1c87da3ae464","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"bold"}],"text":"状态机设计"},{"type":"text","text":":记录拥有"},{"type":"text","marks":[{"type":"code"}],"text":"PENDING -> UPLOADING -> SUCCESS/FAILED"},{"type":"text","text":"的状态流转。这防止了重复上传和数据冲突。"}]}]},{"type":"listItem","attrs":{"id":"1bbd0817-92cb-4caa-8352-095eb4b1bba9"},"content":[{"type":"paragraph","attrs":{"id":"fa4de074-53bb-4053-be56-4855d3041bd9","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"bold"}],"text":"重试机制"},{"type":"text","text":":设置"},{"type":"text","marks":[{"type":"code"}],"text":"retryCount"},{"type":"text","text":",限制最大重试次数(如3次),避免死循环和网络风暴。"}]}]},{"type":"listItem","attrs":{"id":"cbc896c5-117c-4fff-8762-3ca0b5ff3a63"},"content":[{"type":"paragraph","attrs":{"id":"158b2cca-321d-4e84-b3d3-7248a4fd1c8a","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"bold"}],"text":"网络监听"},{"type":"text","text":":使用"},{"type":"text","marks":[{"type":"code"}],"text":"@react-native-community/netinfo"},{"type":"text","text":"监听网络变化。一旦网络恢复,自动触发"},{"type":"text","marks":[{"type":"code"}],"text":"syncPendingRecords"},{"type":"text","text":",实现“无感同步”。"}]}]},{"type":"listItem","attrs":{"id":"62310b1f-cd1f-4614-9a98-b5cd6dd9b5d8"},"content":[{"type":"paragraph","attrs":{"id":"1baeb7cb-e9ba-4858-a491-21302381ca85","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"bold"}],"text":"事务(Transaction)"},{"type":"text","text":":Realm的写入操作必须在"},{"type":"text","marks":[{"type":"code"}],"text":"realm.write()"},{"type":"text","text":"中进行,确保数据一致性。"}]}]}]},{"type":"heading","attrs":{"id":"6e481ed6-f7db-4fbe-a92d-adbf47978a8b","textAlign":"inherit","indent":0,"level":2,"isHoverDragHandle":false},"content":[{"type":"text","text":"4.2 签收流程时序图 "}]},{"type":"image","attrs":{"id":"96f190a5-51ea-4601-a72f-ed9ecf183744","src":"https://developer.qcloudimg.com/http-save/audit-10133498/6ffe3f148bbf622ccc78a5533e73a290.png","extension":"png","align":"center","alt":"","showAlt":false,"href":"","boxShadow":"","width":1012,"aspectRatio":"1.026358","status":"success","showText":true,"isPercentage":false,"percentage":0,"isHoverDragHandle":false}},{"type":"heading","attrs":{"id":"0959e2c6-554a-48c3-9675-ce8b091d6ca2","textAlign":"inherit","indent":0,"level":1,"isHoverDragHandle":false},"content":[{"type":"text","text":"五、性能与安全优化 "}]},{"type":"heading","attrs":{"id":"3a917ed7-912b-4fce-bcab-2345a7db11b8","textAlign":"inherit","indent":0,"level":2,"isHoverDragHandle":false},"content":[{"type":"text","text":"5.1 内存泄漏防治 "}]},{"type":"paragraph","attrs":{"id":"97a5245a-f758-4ddf-8486-ca408f8589ca","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"React Native中图片处理极易引发OOM(Out Of Memory)。"}]},{"type":"bulletList","attrs":{"id":"e7f06fc8-44ed-4e0b-ad30-66b03600dac5","isHoverDragHandle":false},"content":[{"type":"listItem","attrs":{"id":"33a3af47-efda-4658-b54b-aae037453269"},"content":[{"type":"paragraph","attrs":{"id":"0dd846dd-733a-4e77-97d5-6219fc86a3d0","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"bold"}],"text":"及时释放"},{"type":"text","text":":在"},{"type":"text","marks":[{"type":"code"}],"text":"componentWillUnmount"},{"type":"text","text":"或"},{"type":"text","marks":[{"type":"code"}],"text":"useEffect"},{"type":"text","text":"的清理函数中,释放不再使用的图片资源。"}]}]},{"type":"listItem","attrs":{"id":"28fa8dad-d9bf-4ffd-8219-8d12c789b49e"},"content":[{"type":"paragraph","attrs":{"id":"20fd05d4-81d4-4ee3-9e79-dd2f57ae55f3","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"bold"}],"text":"避免Base64大对象"},{"type":"text","text":":如果照片非常多,不建议将所有Base64都存在一个数组中。建议压缩后立即上传(在线时)或写入文件(离线时),只在UI上展示缩略图。"}]}]}]},{"type":"heading","attrs":{"id":"8db9f708-8f43-4b5f-bd9e-542ac54b489c","textAlign":"inherit","indent":0,"level":2,"isHoverDragHandle":false},"content":[{"type":"text","text":"5.2 数据安全 "}]},{"type":"bulletList","attrs":{"id":"886c1be9-c836-4ac8-ac5a-92edd0d39499","isHoverDragHandle":false},"content":[{"type":"listItem","attrs":{"id":"8141070c-8b8d-4466-9651-fb791c7d2c13"},"content":[{"type":"paragraph","attrs":{"id":"5343483f-c3e6-4bba-ab62-2599a1b5fcc8","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"bold"}],"text":"本地加密"},{"type":"text","text":":Realm支持数据库加密。在初始化Realm时传入"},{"type":"text","marks":[{"type":"code"}],"text":"encryptionKey"},{"type":"text","text":",防止手机Root后数据被盗。"}]}]},{"type":"listItem","attrs":{"id":"ce926a74-cdb5-4122-a896-b2932d8314fd"},"content":[{"type":"paragraph","attrs":{"id":"065e9897-3b2e-43ee-9643-2488be42da58","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"bold"}],"text":"HTTPS"},{"type":"text","text":":所有上传接口必须使用HTTPS,防止签名数据被中间人劫持。"}]}]},{"type":"listItem","attrs":{"id":"07758e20-3e07-4e36-9f1c-d5f25e9d819d"},"content":[{"type":"paragraph","attrs":{"id":"33546d8c-3fff-424e-82a4-6e6b341f8fd2","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"bold"}],"text":"法律合规"},{"type":"text","text":":在首次启动App或进入签收页时,必须弹窗告知用户“本次签收将采集电子签名及现场照片,作为运输凭证”,获得用户明确授权。"}]}]}]},{"type":"heading","attrs":{"id":"ccb1a0c3-c8d1-49c0-90e5-e7f647ced256","textAlign":"inherit","indent":0,"level":1,"isHoverDragHandle":false},"content":[{"type":"text","text":"总结 "}]},{"type":"paragraph","attrs":{"id":"e49f4ade-24d5-49a2-80d4-10be675e321e","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"本文详细介绍了基于React Native构建TMS司机端签收模块的全过程。我们不仅实现了基础的电子签名和拍照功能,更重要的是构建了一套"},{"type":"text","marks":[{"type":"bold"}],"text":"健壮的离线优先架构"},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"id":"94ef54d9-56a7-4fae-9f60-78f87086cd4d","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"通过本文,你掌握了:"}]},{"type":"bulletList","attrs":{"id":"01a582ef-d486-4d6b-a751-574f96a360bb","isHoverDragHandle":false},"content":[{"type":"listItem","attrs":{"id":"35cdb3a8-fb67-421e-9c0d-a80ab88da9f0"},"content":[{"type":"paragraph","attrs":{"id":"7f24a622-4bbd-406f-bca0-22e942be8a4b","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"bold"}],"text":"高性能手写板集成"},{"type":"text","text":":利用"},{"type":"text","marks":[{"type":"code"}],"text":"react-native-signature-canvas"},{"type":"text","text":"实现流畅签名,并处理Base64数据流。"}]}]},{"type":"listItem","attrs":{"id":"bf819cdc-fde9-4231-bbeb-b40247269d7d"},"content":[{"type":"paragraph","attrs":{"id":"9ab5002d-e49b-4f92-b08b-83f3bbecdba7","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"bold"}],"text":"图像工程化技巧"},{"type":"text","text":":通过"},{"type":"text","marks":[{"type":"code"}],"text":"ImageResizer"},{"type":"text","text":"进行参数调优,在保证证据清晰度的前提下最大化压缩比,提升上传体验。"}]}]},{"type":"listItem","attrs":{"id":"3fefc6b2-5f29-452c-a356-4294841e3cda"},"content":[{"type":"paragraph","attrs":{"id":"b62e9735-d4d4-4311-8474-9d41342d57b0","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"bold"}],"text":"离线数据架构"},{"type":"text","text":":使用Realm数据库构建本地队列,结合NetInfo实现网络状态感知与自动重试机制,彻底解决弱网环境下的数据可靠性问题。"}]}]},{"type":"listItem","attrs":{"id":"95b9b568-c94c-4c7f-bd5d-e4030a5482c9"},"content":[{"type":"paragraph","attrs":{"id":"d46dc62c-1a52-4bda-91f5-fc8ee0ac438b","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","marks":[{"type":"bold"}],"text":"业务流程闭环"},{"type":"text","text":":理解了签收数据如何触发后续的结算流程,实现真正的数字化回单管理。"}]}]}]},{"type":"paragraph","attrs":{"id":"c245d832-5e5e-47b8-b98d-3c702b7adcd0","textAlign":"inherit","indent":0,"color":null,"background":null,"isHoverDragHandle":false},"content":[{"type":"text","text":"这套方案确保了司机在任何极端环境下都能完成交付,财务部门能第一时间获取有效凭证,显著缩短了回单流转周期,减少了因回单丢失或模糊导致的财务纠纷。"}]}]}","createTime":1782915595,"ext":{"closeTextLink":0,"comment_ban":0,"description":"","focusRead":0},"favNum":0,"html":"","isOriginal":0,"likeNum":1,

相关文章

精彩推荐