基于React Native鸿蒙跨平台库存记录(InventoryRecord)和库位(StorageLocation)等核心数据结构,实现商品入库、条形码扫描、库存记录等核心功能
本项目基于React Native开发跨平台仓库管理应用,支持iOS、Android和鸿蒙系统。采用函数式组件架构,通过TypeScript类型系统定义商品(Product)、库存记录(InventoryRecord)和库位(StorageLocation)等核心数据结构。使用Hooks管理状态,实现商品入库、条形码扫描、库存记录等核心功能。应用采用模块化设计,包含数据验证、用户交互反馈等优化措施
欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。
本项目采用React Native函数式组件架构,以WarehouseManagementApp为核心组件,实现了仓库管理的完整功能流程。架构设计遵循模块化原则,将数据结构、状态管理和业务逻辑清晰分离,便于维护和扩展。
核心技术栈
- React Native:跨平台移动应用开发框架,支持iOS、Android和鸿蒙系统
- TypeScript:提供类型安全,增强代码可维护性和开发体验
- Hooks API:使用useState进行状态管理,简化组件逻辑
- Flexbox:实现响应式布局,适配不同屏幕尺寸
- Base64图标:内置图标资源,减少网络请求,提升加载速度
- Dimensions API:获取屏幕尺寸,实现精细化布局控制
商品类型(Product)
type Product = {
id: string;
name: string;
barcode: string;
quantity: number;
location: string;
category: string;
};
该类型设计合理,包含了仓库管理所需的核心商品属性:
id:唯一标识符,确保数据唯一性name:商品名称,便于识别barcode:条形码,用于扫描识别quantity:库存数量,核心管理指标location:存放位置,便于库位管理category:分类信息,便于商品归类
库存记录类型(InventoryRecord)
type InventoryRecord = {
id: string;
productId: string;
operation: '入库' | '出库';
quantity: number;
timestamp: string;
operator: string;
};
库存记录类型设计全面,包含了库存操作的关键信息:
id:记录唯一标识productId:关联商品ID,建立数据关联operation:操作类型,使用联合类型确保类型安全quantity:操作数量,记录库存变动timestamp:操作时间,用于追溯operator:操作人,明确责任
库位类型(StorageLocation)
type StorageLocation = {
id: string;
code: string;
capacity: number;
currentStock: number;
isAvailable: boolean;
};
库位类型设计合理,包含了仓库管理的必要信息:
id:库位唯一标识code:库位编码,便于定位capacity:容量信息,用于库存规划currentStock:当前库存,实时监控isAvailable:可用状态,确保有效分配
核心状态
应用使用useState钩子管理五个核心状态:
products:商品列表,支持动态更新inventoryRecords:库存记录列表,支持动态更新storageLocations:库位列表,使用常量状态scannedBarcode:扫描的条形码,支持用户输入newQuantity:新的数量信息,用于库存操作
状态更新
- 条形码扫描:通过handleScanBarcode函数,根据条形码查找商品信息
- 入库操作:通过handleStockIn函数,更新商品库存和库位信息
- 状态更新:通过setProducts和setInventoryRecords函数,更新商品和库存记录状态
- 用户输入:通过TextInput的onChangeText事件,实时更新scannedBarcode和newQuantity状态
状态管理
- 不可变数据模式:使用扩展运算符(
...)创建新状态,避免直接修改原状态 - 数据验证:在进行库存操作前进行数据完整性检查
- 状态清理:在操作完成后,清理临时状态,保持界面整洁
- 批量更新:在状态更新时使用map方法批量处理,确保数据一致性
核心业务
- 商品管理:展示商品信息,包含库存数量和存放位置
- 条形码扫描:通过扫描条形码快速识别商品
- 入库操作:处理商品入库,包括数量输入和库位分配
- 库存记录:记录所有库存操作,便于追溯
- 库位管理:根据库位容量和可用性,智能分配存放位置
核心业务
const handleStockIn = (productId: string) => {
Alert.prompt(
'入库操作',
'请输入入库数量:',
[
{ text: '取消', style: 'cancel' },
{
text: '确定',
onPress: (quantity) => {
if (quantity && parseInt(quantity) > 0) {
const product = products.find(p => p.id === productId);
if (product) {
// 分配库位
const availableLocation = storageLocations.find(loc =>
loc.isAvailable && loc.currentStock + parseInt(quantity) <= loc.capacity
);
if (availableLocation) {
// 更新产品库存和位置
setProducts(products.map(p =>
p.id === productId
? {
...p,
quantity: p.quantity + parseInt(quantity),
location: availableLocation.code
}
: p
));
// 更新库位库存
// 这里简化处理,实际应该更新storageLocations状态
// 添加库存记录
const record: InventoryRecord = {
id: (inventoryRecords.length + 1).toString(),
productId: productId,
operation: '入库',
quantity: parseInt(quantity),
timestamp: new Date().toLocaleString('zh-CN'),
operator: '当前用户'
};
setInventoryRecords([...inventoryRecords, record]);
Alert.alert('成功', `商品已入库到${availableLocation.code}库位`);
setScannedBarcode('');
setNewQuantity('');
} else {
Alert.alert('提示', '没有合适的库位,请检查库存容量');
}
}
}
}
}
],
'plain-text'
);
};
数据流
- 单向数据流:状态 → 视图 → 用户操作 → 状态更新
- 数据验证:在进行库存操作前进行数据完整性检查
- 业务逻辑封装:将复杂计算逻辑封装在专用函数中,提高代码可读性
- 用户交互反馈:使用Alert组件提供操作确认和信息提示,提升用户体验
组件
- 核心组件:SafeAreaView、View、Text、ScrollView、TouchableOpacity、TextInput等在鸿蒙系统上有对应实现
- API兼容性:Dimensions API在鸿蒙系统中可正常使用,确保布局适配
- Alert组件:鸿蒙系统支持Alert组件的基本功能,但样式可能有所差异
- Alert.prompt:需要注意鸿蒙系统对Alert.prompt的支持情况,可能需要自定义实现
资源
- Base64图标:在鸿蒙系统中同样支持,可减少网络请求,提升性能
- 内存管理:鸿蒙系统对内存使用更为严格,需注意资源释放
- 计算性能:对于库位分配等操作,鸿蒙系统的处理性能与其他平台相当
性能
-
渲染性能:
- 避免不必要的重渲染,合理使用React.memo
- 对于长商品列表,建议使用FlatList替代ScrollView
- 优化组件结构,减少嵌套层级
-
数据处理:
- 缓存计算结果,避免重复计算
- 优化库位分配算法,特别是可用库位的查找
- 考虑使用useMemo缓存计算结果,提高性能
-
内存管理:
- 及时释放不再使用的资源
- 避免内存泄漏,特别是在处理多个商品和库存记录时
- 合理使用缓存策略,平衡性能和内存占用
- 条件渲染:使用Platform API检测平台,针对不同平台使用不同实现
- 样式适配:考虑鸿蒙系统的设计规范,调整UI样式以符合平台特性
- 权限处理:鸿蒙系统的权限管理与Android有所不同,需单独处理
- 鸿蒙特性:充分利用鸿蒙系统的分布式能力,实现多设备协同
- 扫码功能:针对鸿蒙系统的扫码API,调整条形码扫描实现
1. 类型定义
- 使用枚举类型:将操作类型、商品分类等使用枚举类型替代字符串,提高代码可读性和可维护性
- 类型扩展:考虑使用接口继承,增强类型系统的表达能力
- 类型守卫:添加更多类型守卫,确保运行时类型安全
- 国际化支持:考虑添加国际化支持,特别是操作类型等术语
2. 状态管理
- 状态分离:对于复杂状态,考虑使用useReducer或状态管理库(如Redux、Zustand)
- 状态持久化:实现状态持久化,使用AsyncStorage存储商品和库存数据
- 计算属性:使用useMemo缓存计算结果,避免重复计算
- 批量操作:支持批量库存操作,提高管理效率
3. 组件化
- 组件拆分:将大型组件拆分为更小的可复用组件,如ProductCard、InventoryRecordItem、StorageLocationCard等
- 自定义Hook:提取重复的状态逻辑到自定义Hook,如useInventoryManager、useBarcodeScanner等
- 高阶组件:使用高阶组件处理横切关注点,如错误边界、加载状态等
- 表单组件:封装表单输入组件,提高代码复用性
4. 业务逻辑
- 服务层分离:将业务逻辑分离到服务层,提高代码可测试性
- 错误处理:增强错误处理机制,提供更友好的错误提示
- 实时更新:考虑添加实时库存更新,提升管理效率
- 智能算法:实现更智能的库位分配算法,考虑更多因素如商品分类、存取频率等
5. 性能
- 列表优化:使用FlatList的性能优化特性,如getItemLayout、initialNumToRender等
- 网络优化:实现请求防抖和节流,减少网络请求频率
- Bundle优化:使用代码分割和Tree Shaking,减少应用体积
- 启动优化:优化应用启动速度,减少初始加载时间
1. 代码
- 模块化设计:采用模块化设计,分离平台特定代码
- 配置管理:使用配置文件管理不同平台的差异
- 目录结构:合理组织目录结构,便于维护和扩展
- 安全分区:对于商品信息等敏感数据,采用安全分区存储
仓库管理作为供应链核心环节,要求应用具备条码识别的精准性、库存操作的高效性、库位管理的智能化三大核心能力。本文将深度拆解基于 React Native 开发的仓库管理应用,剖析其商品建模、入库流程、库位分配的核心技术实现,并提供完整的鸿蒙(HarmonyOS)ArkTS 跨端适配方案,为仓储类应用的跨端开发提供可落地的技术参考。
1. 仓储场景
仓库管理系统区别于普通应用的核心在于商品条码化、库存操作可追溯、库位资源动态化。代码通过 TypeScript 类型系统构建了精准贴合仓储管理场景的领域模型,充分体现了仓储管理的行业特性:
// 商品类型
type Product = {
id: string;
name: string;
barcode: string;
quantity: number;
location: string;
category: string;
};
// 库存记录类型
type InventoryRecord = {
id: string;
productId: string;
operation: '入库' | '出库';
quantity: number;
timestamp: string;
operator: string;
};
// 库位类型
type StorageLocation = {
id: string;
code: string;
capacity: number;
currentStock: number;
isAvailable: boolean;
};
模型设计的仓储场景适配性分析:
- 商品维度条码化:
Product模型以barcode作为核心标识,符合仓储管理"一物一码"的标准化管理要求,是扫码入库的基础; - 库存操作可追溯:
InventoryRecord模型记录操作类型、数量、时间、操作人员等全维度信息,满足仓储管理的审计和追溯需求; - 操作类型约束化:采用"入库/出库"字面量类型约束,避免非法操作类型输入,保证库存操作的规范性;
- 库位资源结构化:
StorageLocation模型包含容量、当前库存、可用状态等核心维度,为智能库位分配提供数据支撑; - 空间管理精细化:
capacity和currentStock字段量化库位容量使用情况,是库存分配算法的核心依据; - 可用性状态化:
isAvailable字段标记库位可用状态,保证库存分配的有效性,避免超容量入库; - 位置关联明确化:商品模型通过
location字段关联库位编码,实现商品与库位的精准映射; - 类型安全保障:所有核心字段均有明确类型定义,避免数据类型错误导致的库存管理混乱;
- 操作记录完整化:库存记录包含操作人、时间戳等元数据,满足仓储管理的合规性要求。
2. 仓储管理
仓储管理的核心痛点是条码识别的准确性、入库操作的高效性、库位分配的智能化,代码通过轻量化但专业的逻辑实现了仓储管理的核心业务流程:
(1)条码扫描
条码扫描是仓储入库的首要环节,必须确保条码识别的准确性和商品匹配的高效性:
const handleScanBarcode = () => {
if (!scannedBarcode.trim()) {
Alert.alert('提示', '请先扫描或输入条形码');
return;
}
const product = products.find(p => p.barcode === scannedBarcode);
if (product) {
Alert.alert(
'扫描结果',
`商品: ${product.name}\n` +
`当前库存: ${product.quantity}\n` +
`存放位置: ${product.location}`,
[
{ text: '取消', style: 'cancel' },
{
text: '入库',
onPress: () => handleStockIn(product.id)
}
]
);
} else {
Alert.alert('提示', '未找到对应商品,请检查条形码是否正确');
}
};
条码扫描逻辑的仓储场景适配性:
- 空值校验前置:先校验条码输入是否为空,避免无效扫描操作,符合仓储操作的严谨性要求;
- 精准商品匹配:通过条码精准匹配商品信息,保证入库操作的准确性,避免扫错码导致的库存错误;
- 信息完整展示:扫描成功后展示商品名称、当前库存、存放位置等核心信息,为入库决策提供完整参考;
- 操作流程引导:直接提供入库操作选项,简化操作流程,提升仓储作业效率;
- 异常友好提示:未找到商品时给出明确提示,引导检查条码正确性,降低操作失误率;
- 交互流程优化:采用弹窗形式展示扫描结果,符合移动端仓储操作的交互习惯;
- 数据一致性保障:基于现有商品数据进行匹配,保证库存数据的一致性。
(2)智能入库
入库操作是仓储管理的核心环节,需基于库位容量和可用性实现最优分配,直接影响库存管理效率:
const handleStockIn = (productId: string) => {
Alert.prompt(
'入库操作',
'请输入入库数量:',
[
{ text: '取消', style: 'cancel' },
{
text: '确定',
onPress: (quantity) => {
if (quantity && parseInt(quantity) > 0) {
const product = products.find(p => p.id === productId);
if (product) {
// 分配库位
const availableLocation = storageLocations.find(loc =>
loc.isAvailable && loc.currentStock + parseInt(quantity) <= loc.capacity
);
if (availableLocation) {
// 更新产品库存和位置
setProducts(products.map(p =>
p.id === productId
? {
...p,
quantity: p.quantity + parseInt(quantity),
location: availableLocation.code
}
: p
));
// 更新库位库存
// 这里简化处理,实际应该更新storageLocations状态
// 添加库存记录
const record: InventoryRecord = {
id: (inventoryRecords.length + 1).toString(),
productId: productId,
operation: '入库',
quantity: parseInt(quantity),
timestamp: new Date().toLocaleString('zh-CN'),
operator: '当前用户'
};
setInventoryRecords([...inventoryRecords, record]);
Alert.alert('成功', `商品已入库到${availableLocation.code}库位`);
setScannedBarcode('');
setNewQuantity('');
} else {
Alert.alert('提示', '没有合适的库位,请检查库存容量');
}
}
}
}
}
],
'plain-text'
);
};
入库与库位分配逻辑的智能化设计:
- 数量有效性校验:校验入库数量是否为正数,避免无效或负数入库,保证库存数据的准确性;
- 智能库位筛选:自动筛选可用且容量充足的库位,避免超容量入库,符合仓储管理的容量管控要求;
- 库存原子更新:采用不可变更新模式更新商品库存,避免直接修改原数据,保证数据一致性;
- 位置自动关联:入库成功后自动更新商品的库位信息,实现商品与库位的动态绑定;
- 操作记录完整化:自动生成入库操作记录,包含时间戳和操作人员,满足仓储审计需求;
- 结果明确反馈:提示具体入库的库位编码,便于操作人员确认和后续查找;
- 表单状态重置:入库成功后清空条码和数量输入,支持连续入库操作,提升作业效率;
- 异常场景处理:无合适库位时给出明确提示,引导检查库位容量,避免操作阻塞;
- 用户体验优化:采用弹窗输入数量的方式,符合移动端快速操作的交互习惯。
(3)手动录入
仓储管理中需支持手动录入补充,满足条码扫描不便或新增商品的入库需求:
const handleManualEntry = () => {
if (!scannedBarcode.trim() || !newQuantity.trim()) {
Alert.alert('提示', '请填写完整的商品信息');
return;
}
const quantity = parseInt(newQuantity);
if (isNaN(quantity) || quantity <= 0) {
Alert.alert('提示', '请输入有效的数量');
return;
}
const existingProduct = products.find(p => p.barcode === scannedBarcode);
if (existingProduct) {
// 更新现有商品
setProducts(products.map(p =>
p.id === existingProduct.id
? { ...p, quantity: p.quantity + quantity }
: p
));
} else {
// 创建新商品
const newProduct: Product = {
id: (products.length + 1).toString(),
name: `商品-${scannedBarcode.slice(-4)}`,
barcode: scannedBarcode,
quantity: quantity,
location: '待分配',
category: '未分类'
};
setProducts([...products, newProduct]);
}
// 添加库存记录
const record: InventoryRecord = {
id: (inventoryRecords.length + 1).toString(),
productId: existingProduct?.id || (products.length + 1).toString(),
operation: '入库',
quantity: quantity,
timestamp: new Date().toLocaleString('zh-CN'),
operator: '当前用户'
};
setInventoryRecords([...inventoryRecords, record]);
Alert.alert('成功', '库存已更新');
setScannedBarcode('');
setNewQuantity('');
};
手动录入逻辑的实用性设计:
- 全字段校验:校验条码和数量输入的完整性,保证入库信息的有效性;
- 数量合法性校验:确保数量为有效正数,避免非法数值导致的库存错误;
- 商品存在性判断:区分已有商品和新增商品,分别处理库存更新逻辑;
- 存量商品更新:对已有商品仅更新库存数量,保留其他信息不变;
- 新增商品创建:自动生成商品名称和基础信息,支持快速建档入库;
- 状态标记明确:新增商品位置标记为"待分配",便于后续库位调整;
- 操作记录统一化:无论新增还是更新,均生成标准化的库存操作记录;
- 反馈机制完善:操作成功后给出明确提示,提升操作人员的确认感;
- 表单重置优化:清空输入框,支持连续录入操作,提升批量入库效率;
- 数据一致性保障:采用不可变更新模式,避免直接修改原数据导致的状态异常。
3. 仓储管理场景
仓储管理应用的视觉设计围绕效率性、辨识度、易用性三个核心维度展开,贴合仓储作业的业务特性和用户操作习惯:
(1)设计
- 行业化色调体系:采用蓝色系(
#0284c7/#0c4a6e)为主色调,契合仓储管理的专业、严谨的行业属性,传递高效、精准的品牌感知; - 功能模块卡片化:所有核心功能(扫码入库、手动录入、库存列表、库位状态)采用卡片式设计,符合移动端仓储管理的操作分区习惯;
- 信息层级化展示:库存列表中核心信息(商品名称、库存数量)突出展示,详情信息(条码、位置)次级展示,提升信息读取效率;
- 操作按钮场景化:不同功能模块展示对应操作按钮(扫码入库、确认入库),符合仓储管理的业务流程;
- 库位状态可视化:通过颜色指示器直观展示库位可用状态,便于快速识别库位资源;
- 操作类型差异化:入库操作记录以绿色标识,出库(预留)以红色标识,符合仓储管理的视觉识别习惯;
- 底部导航场景化:按扫描、库存、库位、我的分类,贴合仓储管理的核心业务流程;
- 安全区域适配:使用 SafeAreaView 适配异形屏,保证操作界面的完整性;
- 卡片层级化设计:所有功能模块采用卡片式设计+轻微阴影,提升界面层次感,符合移动端管理系统的设计趋势。
(2)交互
- 条码输入优化:扫码区域设计为图标+输入框组合,符合扫码操作的视觉认知;
- 数字输入适配:数量输入框适配数字键盘,提升录入效率;
- 列表滚动优化:库存列表、库位列表、操作记录均支持滚动,适配多商品/多库位管理的展示需求;
- 操作反馈明确化:所有操作均提供明确的结果提示,保证操作人员的感知;
- 底部导航固定展示:核心管理功能入口固定展示,便于操作过程中的功能快速切换;
- 状态指示器直观化:库位可用状态通过圆形指示器展示,提升识别效率;
- 操作记录倒序展示:最新的操作记录优先展示,符合仓储管理的查看习惯;
- 库存数量突出化:库存数量以大号加粗字体展示,便于快速识别库存水平。
将 React Native 仓库管理系统迁移至鸿蒙平台,核心是基于 ArkTS + ArkUI 实现类型系统、状态管理、业务逻辑、视觉交互的对等还原,同时适配鸿蒙的组件特性和布局范式,保证仓储管理体验的一致性和专业性。
1. 架构
鸿蒙端适配遵循类型复用、逻辑对等、体验统一的原则,仓储管理的核心业务逻辑和视觉规范100%复用,仅需适配平台特有API和组件语法,确保仓库管理应用的跨端体验一致性:
@Entry
@Component
struct WarehouseManagementApp {
// 类型定义:对等实现 TypeScript 类型 → 接口定义
interface Product {
id: string;
name: string;
barcode: string;
quantity: number;
location: string;
category: string;
}
interface InventoryRecord {
id: string;
productId: string;
operation: '入库' | '出库';
quantity: number;
timestamp: string;
operator: string;
}
interface StorageLocation {
id: string;
code: string;
capacity: number;
currentStock: number;
isAvailable: boolean;
}
// 状态管理:对等实现 useState → @State
@State products: Product[] = [/* 初始商品数据 */];
@State inventoryRecords: InventoryRecord[] = [/* 初始库存记录 */];
@State storageLocations: StorageLocation[] = [/* 初始库位数据 */];
@State scannedBarcode: string = '';
@State newQuantity: string = '';
// 业务逻辑:完全复用 RN 端的仓储管理核心逻辑
handleScanBarcode(): void {/* 条码扫描 */}
handleStockIn(productId: string): void {/* 入库操作 */}
handleManualEntry(): void {/* 手动录入 */}
// 页面构建:镜像 RN 端布局结构,适配鸿蒙组件特性
build() {
Column() {
// 头部区域
// 扫码入库区
// 手动录入区
// 库存列表
// 库位状态
// 操作记录
// 底部导航
}
}
}
| React Native 特性 | 鸿蒙 ArkUI 对应实现 | 仓储场景适配关键说明 |
|---|---|---|
| TypeScript 类型定义 | TypeScript 接口定义 | 商品、库存记录、库位等类型完全复用,保证仓储管理数据结构一致性 |
useState |
@State 装饰器 |
商品列表、库存记录、表单状态等管理逻辑完全复用,保持仓储流程一致性 |
| 入库算法 | 函数逻辑完全复用 | 库位分配、库存更新、操作记录规则一致,保证仓储管理的准确性 |
TextInput |
TextInput 组件 |
输入框属性适配,数字输入类型对等实现,保持录入体验一致 |
TouchableOpacity |
Button + onClick |
所有可点击区域通过 onClick 事件实现,保持操作交互一致性 |
Alert.alert |
AlertDialog.show |
条码扫描、入库操作等弹窗逻辑对等,符合仓储管理操作习惯 |
StyleSheet |
链式样式 | 蓝色系主色调、商品卡片样式等视觉规范100%复用 |
Array.map |
ForEach 组件 |
商品/库存/库位列表渲染逻辑一致,适配多商品/多库位展示需求 |
ScrollView |
Scroll 组件 |
滚动容器语法差异,功能一致,适配长列表展示 |
| 状态颜色映射 | 函数逻辑完全复用 | 库位状态-颜色映射规则一致,保证仓储管理状态的视觉认知一致性 |
| 底部导航 | Position.Fixed |
导航栏定位语法差异,效果一致,保证仓储管理操作的便捷性 |
3. 鸿蒙代码
// 鸿蒙 ArkTS 完整实现 - 仓库管理系统
@Entry
@Component
struct WarehouseManagementApp {
// 类型定义
interface Product {
id: string;
name: string;
barcode: string;
quantity: number;
location: string;
category: string;
}
interface InventoryRecord {
id: string;
productId: string;
operation: '入库' | '出库';
quantity: number;
timestamp: string;
operator: string;
}
interface StorageLocation {
id: string;
code: string;
capacity: number;
currentStock: number;
isAvailable: boolean;
}
// 状态管理
@State products: Product[] = [
{
id: '1',
name: 'iPhone 15 Pro',
barcode: '123456789012',
quantity: 50,
location: 'A01-01',
category: '电子产品'
},
{
id: '2',
name: 'MacBook Air',
barcode: '234567890123',
quantity: 30,
location: 'A02-05',
category: '电脑设备'
}
];
@State inventoryRecords: InventoryRecord[] = [
{
id: '1',
productId: '1',
operation: '入库',
quantity: 20,
timestamp: '2023-12-01 10:30',
operator: '张管理员'
}
];
@State storageLocations: StorageLocation[] = [
{
id: 'loc1',
code: 'A01-01',
capacity: 100,
currentStock: 50,
isAvailable: true
},
{
id: 'loc2',
code: 'A02-05',
capacity: 80,
currentStock: 30,
isAvailable: true
}
];
@State scannedBarcode: string = '';
@State newQuantity: string = '';
// 处理条码扫描
handleScanBarcode(): void {
if (!this.scannedBarcode.trim()) {
AlertDialog.show({
title: '提示',
message: '请先扫描或输入条形码',
confirm: { value: '确定' }
});
return;
}
const product = this.products.find(p => p.barcode === this.scannedBarcode);
if (product) {
AlertDialog.show({
title: '扫描结果',
message: `商品: ${product.name}\n当前库存: ${product.quantity}\n存放位置: ${product.location}`,
buttons: [
{ value: '取消', action: () => {} },
{
value: '入库',
action: () => this.handleStockIn(product.id)
}
]
});
} else {
AlertDialog.show({
title: '提示',
message: '未找到对应商品,请检查条形码是否正确',
confirm: { value: '确定' }
});
}
}
// 处理入库操作
handleStockIn(productId: string): void {
TextPrompt.show({
title: '入库操作',
message: '请输入入库数量:',
confirm: {
value: '确定',
action: (quantity: string) => {
if (quantity && parseInt(quantity) > 0) {
const product = this.products.find(p => p.id === productId);
if (product) {
// 分配库位
const availableLocation = this.storageLocations.find(loc =>
loc.isAvailable && loc.currentStock + parseInt(quantity) <= loc.capacity
);
if (availableLocation) {
// 更新产品库存和位置
this.products = this.products.map(p =>
p.id === productId
? {
...p,
quantity: p.quantity + parseInt(quantity),
location: availableLocation.code
}
: p
);
// 添加库存记录
const record: InventoryRecord = {
id: (this.inventoryRecords.length + 1).toString(),
productId: productId,
operation: '入库',
quantity: parseInt(quantity),
timestamp: new Date().toLocaleString('zh-CN'),
operator: '当前用户'
};
this.inventoryRecords = [...this.inventoryRecords, record];
AlertDialog.show({
title: '成功',
message: `商品已入库到${availableLocation.code}库位`,
confirm: { value: '确定' }
});
this.scannedBarcode = '';
this.newQuantity = '';
} else {
AlertDialog.show({
title: '提示',
message: '没有合适的库位,请检查库存容量',
confirm: { value: '确定' }
});
}
}
}
}
},
cancel: { value: '取消' }
});
}
// 处理手动录入
handleManualEntry(): void {
if (!this.scannedBarcode.trim() || !this.newQuantity.trim()) {
AlertDialog.show({
title: '提示',
message: '请填写完整的商品信息',
confirm: { value: '确定' }
});
return;
}
const quantity = parseInt(this.newQuantity);
if (isNaN(quantity) || quantity <= 0) {
AlertDialog.show({
title: '提示',
message: '请输入有效的数量',
confirm: { value: '确定' }
});
return;
}
const existingProduct = this.products.find(p => p.barcode === this.scannedBarcode);
if (existingProduct) {
// 更新现有商品
this.products = this.products.map(p =>
p.id === existingProduct.id
? { ...p, quantity: p.quantity + quantity }
: p
);
} else {
// 创建新商品
const newProduct: Product = {
id: (this.products.length + 1).toString(),
name: `商品-${this.scannedBarcode.slice(-4)}`,
barcode: this.scannedBarcode,
quantity: quantity,
location: '待分配',
category: '未分类'
};
this.products = [...this.products, newProduct];
}
// 添加库存记录
const record: InventoryRecord = {
id: (this.inventoryRecords.length + 1).toString(),
productId: existingProduct?.id || (this.products.length + 1).toString(),
operation: '入库',
quantity: quantity,
timestamp: new Date().toLocaleString('zh-CN'),
operator: '当前用户'
};
this.inventoryRecords = [...this.inventoryRecords, record];
AlertDialog.show({
title: '成功',
message: '库存已更新',
confirm: { value: '确定' }
});
this.scannedBarcode = '';
this.newQuantity = '';
}
build() {
Column()
.flex(1)
.backgroundColor('#f0f9ff')
.safeArea(true) {
// 头部区域
Column()
.padding(16)
.backgroundColor('#ffffff')
.borderBottom({ width: 1, color: '#bae6fd' }) {
Text('仓库管理')
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor('#0c4a6e')
.marginBottom(4);
Text('扫码入库,智能管理')
.fontSize(14)
.fontColor('#0284c7');
}
// 滚动内容区
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('#0c4a6e')
.marginBottom(12);
// 扫描区域
Row()
.alignItems(ItemAlign.Center)
.backgroundColor('#f0f9ff')
.borderRadius(8)
.paddingLeft(12)
.paddingRight(12)
.marginBottom(12) {
Text('📷')
.fontSize(20)
.fontColor('#64748b')
.marginRight(8);
TextInput({
placeholder: '扫描或输入条形码',
text: this.scannedBarcode
})
.flexGrow(1)
.paddingTop(12)
.paddingBottom(12)
.fontSize(14)
.fontColor('#0c4a6e')
.onChangeText((text) => {
this.scannedBarcode = text;
});
}
// 扫描按钮
Button('扫描条码')
.backgroundColor('#0284c7')
.paddingTop(14)
.paddingBottom(14)
.borderRadius(8)
.fontSize(16)
.fontWeight(FontWeight.SemiBold)
.fontColor(Color.White)
.onClick(() => this.handleScanBarcode());
}
// 手动录入卡片
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('#0c4a6e')
.marginBottom(12);
// 数量输入框
TextInput({
placeholder: '入库数量',
text: this.newQuantity
})
.borderWidth(1)
.borderColor('#bae6fd')
.borderRadius(8)
.padding(12)
.fontSize(14)
.backgroundColor('#f0f9ff')
.marginBottom(12)
.type(InputType.Number)
.onChangeText((text) => {
this.newQuantity = text;
});
// 确认入库按钮
Button('确认入库')
.backgroundColor('#10b981')
.paddingTop(14)
.paddingBottom(14)
.borderRadius(8)
.fontSize(16)
.fontWeight(FontWeight.SemiBold)
.fontColor(Color.White)
.onClick(() => this.handleManualEntry());
}
// 库存列表卡片
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('#0c4a6e')
.marginBottom(12);
// 商品列表
ForEach(this.products, (product: Product) => {
Row()
.justifyContent(FlexAlign.SpaceBetween)
.alignItems(ItemAlign.Center)
.paddingTop(12)
.paddingBottom(12)
.borderBottom({ width: 1, color: '#bae6fd' }) {
// 商品信息
Column()
.flexGrow(1) {
Text(product.name)
.fontSize(14)
.fontWeight(FontWeight.Medium)
.fontColor('#0c4a6e')
.marginBottom(2);
Text(`条码: ${product.barcode}`)
.fontSize(12)
.fontColor('#64748b')
.marginBottom(2);
Text(`位置: ${product.location}`)
.fontSize(12)
.fontColor('#64748b');
}
// 库存信息
Column()
.alignItems(ItemAlign.Center) {
Text(`${product.quantity}`)
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor('#0284c7');
Text('件')
.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('#0c4a6e')
.marginBottom(12);
// 库位列表
ForEach(this.storageLocations, (location: StorageLocation) => {
Row()
.justifyContent(FlexAlign.SpaceBetween)
.alignItems(ItemAlign.Center)
.paddingTop(12)
.paddingBottom(12)
.borderBottom({ width: 1, color: '#bae6fd' }) {
// 库位信息
Column()
.flexGrow(1) {
Text(location.code)
.fontSize(14)
.fontWeight(FontWeight.Medium)
.fontColor('#0c4a6e')
.marginBottom(2);
Text(`容量: ${location.currentStock}/${location.capacity}`)
.fontSize(12)
.fontColor('#64748b');
}
// 库位状态
Column()
.alignItems(ItemAlign.Center) {
Column()
.width(12)
.height(12)
.borderRadius(6)
.backgroundColor(location.isAvailable ? '#10b981' : '#6b7280')
.marginBottom(4);
Text(location.isAvailable ? '可用' : '已满')
.fontSize(12)
.fontColor(Color.White);
}
}
})
}
// 操作记录卡片
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('#0c4a6e')
.marginBottom(12);
// 记录列表(取最近5条,倒序)
ForEach(this.inventoryRecords.slice(-5).reverse(), (record: InventoryRecord) => {
const product = this.products.find(p => p.id === record.productId);
return Column()
.paddingTop(12)
.paddingBottom(12)
.borderBottom({ width: 1, color: '#bae6fd' }) {
// 记录头部
Row()
.justifyContent(FlexAlign.SpaceBetween)
.alignItems(ItemAlign.Center)
.marginBottom(4) {
Text(product?.name || '未知商品')
.fontSize(14)
.fontWeight(FontWeight.Medium)
.fontColor('#0c4a6e');
Text(record.operation)
.fontSize(12)
.fontWeight(FontWeight.Medium)
.fontColor(record.operation === '入库' ? '#10b981' : '#ef4444');
}
// 记录详情
Row()
.justifyContent(FlexAlign.SpaceBetween) {
Text(`数量: ${record.quantity}`)
.fontSize(12)
.fontColor('#64748b');
Text(record.timestamp)
.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('#0c4a6e')
.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: '#bae6fd' })
.paddingTop(12)
.paddingBottom(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: '#0284c7' }) {
Text('👤')
.fontSize(20)
.fontColor('#0284c7')
.marginBottom(4);
Text('我的')
.fontSize(12)
.fontColor('#0284c7')
.fontWeight(FontWeight.Medium);
}
}
}
}
1. 原则
- 仓储模型专业化复用:商品、库存记录、库位等类型定义在两端保持一致,包含条码、容量、可用状态等核心字段,保证仓储管理的专业性;
- 视觉规范100%对齐:蓝色系主色调、商品卡片样式、库位状态颜色映射等视觉属性完全复用,符合仓储管理的行业视觉认知;
- 业务逻辑统一:条码扫描、入库操作、库位分配、手动录入等核心逻辑保持一致,保证仓储管理流程的准确性和高效性;
- 交互体验一致:条码输入、数量录入、按钮操作、弹窗提示等交互逻辑保持一致,降低仓储操作人员的学习成本;
- 行业特性兼容:条码识别、库位分配、库存追溯等仓储行业特性在两端完整实现,满足仓储管理的核心需求;
- 平台特性适配:弹窗展示、输入框类型、点击事件等平台特有API做适配处理,保证仓库管理应用的可用性;
- 性能优化适配:鸿蒙端利用 ForEach 组件的复用机制优化列表渲染性能,RN 端利用数组映射实现高效渲染,均满足大规模库存管理的性能要求。
2. 仓库管理系统
- 条码扫描原生集成:对接设备相机和条码扫描SDK,实现原生扫码功能,提升条码识别效率和准确性;
- 批量操作支持:增加批量入库、批量移库、批量盘点功能,提升仓储作业效率;
- 库存预警机制:设置库存上下限,实现库存不足或超储自动预警,优化库存水平;
- 库位可视化:实现库位二维/三维可视化展示,支持拖拽式库位调整;
- 出入库规则引擎:配置化出入库规则,支持先进先出、后进先出等多种出库策略;
- 盘点功能完善:增加库存盘点、差异调整、盘点报告生成功能,满足周期性盘点需求;
- 数据同步优化:实现离线操作和云端同步,支持网络不稳定环境下的仓储作业;
- 报表分析功能:增加库存周转率、库位利用率、出入库频次等数据分析报表;
- 权限精细化管理:区分管理员、操作员、盘点员等角色,提供精细化的权限控制;
- 电子标签集成:对接RFID/电子标签系统,实现库存的自动化识别和管理;
- 供应商管理:关联供应商信息,实现入库商品的溯源管理;
- 订单关联:对接订单系统,实现按订单拣货、出库的自动化流程;
- 移动打印支持:对接便携打印机,实现库位标签、拣货单的现场打印。
仓库管理系统作为供应链核心数字化工具,其跨端适配的关键在于条码识别精准性、库存操作高效性、体验一致性的三位一体。这份 React Native 实现的仓库管理组件,通过强类型领域建模、智能库位分配逻辑、行业化视觉设计,构建了高效的移动端仓储管理体验;而鸿蒙 ArkTS 端的适配实现,则验证了跨端开发中"逻辑复用、体验统一"的核心原则。
- 仓库管理系统的类型设计需包含商品条码、库存操作记录、库位容量/可用状态等核心字段,保证仓储管理的专业性;
- 条码扫描逻辑需包含空值校验、精准商品匹配、完整信息展示等核心步骤,保证条码识别的准确性和入库操作的高效性;
- 入库算法需基于库位可用性和容量约束实现智能分配,支持手动录入补充,最大化提升仓储作业效率;
- 视觉设计采用蓝色系主色调,功能模块采用卡片式设计,符合仓储管理的专业、严谨的行业视觉认知;
- 跨端适配的核心是仓储管理的行业逻辑复用 + 平台特性适配,无需重写核心业务逻辑;
- 条码输入设计需贴合仓储操作习惯,支持扫码和手动输入两种模式,提升入库操作的灵活性;
- 库存记录需包含完整的操作元数据,满足仓储管理的可追溯性和合规性要求;
- 库位列表需展示编码、容量使用情况、可用状态等核心信息,为库位分配决策提供数据支撑。
真实演示案例代码:
// App.tsx
import React, { useState } from 'react';
import { SafeAreaView, View, Text, StyleSheet, TouchableOpacity, ScrollView, Dimensions, Alert, TextInput } from 'react-native';
// Base64 图标库
const ICONS_BASE64 = {
barcode: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
warehouse: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
location: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
inventory: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
scan: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
check: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
alert: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
info: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
};
const { width, height } = Dimensions.get('window');
// 商品类型
type Product = {
id: string;
name: string;
barcode: string;
quantity: number;
location: string;
category: string;
};
// 库存记录类型
type InventoryRecord = {
id: string;
productId: string;
operation: '入库' | '出库';
quantity: number;
timestamp: string;
operator: string;
};
// 库位类型
type StorageLocation = {
id: string;
code: string;
capacity: number;
currentStock: number;
isAvailable: boolean;
};
// 仓库管理应用组件
const WarehouseManagementApp: React.FC = () => {
const [products, setProducts] = useState<Product[]>([
{
id: '1',
name: 'iPhone 15 Pro',
barcode: '123456789012',
quantity: 50,
location: 'A01-01',
category: '电子产品'
},
{
id: '2',
name: 'MacBook Air',
barcode: '234567890123',
quantity: 30,
location: 'A02-05',
category: '电脑设备'
}
]);
const [inventoryRecords, setInventoryRecords] = useState<InventoryRecord[]>([
{
id: '1',
productId: '1',
operation: '入库',
quantity: 20,
timestamp: '2023-12-01 10:30',
operator: '张管理员'
}
]);
const [storageLocations] = useState<StorageLocation[]>([
{
id: 'loc1',
code: 'A01-01',
capacity: 100,
currentStock: 50,
isAvailable: true
},
{
id: 'loc2',
code: 'A02-05',
capacity: 80,
currentStock: 30,
isAvailable: true
}
]);
const [scannedBarcode, setScannedBarcode] = useState('');
const [newQuantity, setNewQuantity] = useState('');
const handleScanBarcode = () => {
if (!scannedBarcode.trim()) {
Alert.alert('提示', '请先扫描或输入条形码');
return;
}
const product = products.find(p => p.barcode === scannedBarcode);
if (product) {
Alert.alert(
'扫描结果',
`商品: ${product.name}\n` +
`当前库存: ${product.quantity}\n` +
`存放位置: ${product.location}`,
[
{ text: '取消', style: 'cancel' },
{
text: '入库',
onPress: () => handleStockIn(product.id)
}
]
);
} else {
Alert.alert('提示', '未找到对应商品,请检查条形码是否正确');
}
};
const handleStockIn = (productId: string) => {
Alert.prompt(
'入库操作',
'请输入入库数量:',
[
{ text: '取消', style: 'cancel' },
{
text: '确定',
onPress: (quantity) => {
if (quantity && parseInt(quantity) > 0) {
const product = products.find(p => p.id === productId);
if (product) {
// 分配库位
const availableLocation = storageLocations.find(loc =>
loc.isAvailable && loc.currentStock + parseInt(quantity) <= loc.capacity
);
if (availableLocation) {
// 更新产品库存和位置
setProducts(products.map(p =>
p.id === productId
? {
...p,
quantity: p.quantity + parseInt(quantity),
location: availableLocation.code
}
: p
));
// 更新库位库存
// 这里简化处理,实际应该更新storageLocations状态
// 添加库存记录
const record: InventoryRecord = {
id: (inventoryRecords.length + 1).toString(),
productId: productId,
operation: '入库',
quantity: parseInt(quantity),
timestamp: new Date().toLocaleString('zh-CN'),
operator: '当前用户'
};
setInventoryRecords([...inventoryRecords, record]);
Alert.alert('成功', `商品已入库到${availableLocation.code}库位`);
setScannedBarcode('');
setNewQuantity('');
} else {
Alert.alert('提示', '没有合适的库位,请检查库存容量');
}
}
}
}
}
],
'plain-text'
);
};
const handleManualEntry = () => {
if (!scannedBarcode.trim() || !newQuantity.trim()) {
Alert.alert('提示', '请填写完整的商品信息');
return;
}
const quantity = parseInt(newQuantity);
if (isNaN(quantity) || quantity <= 0) {
Alert.alert('提示', '请输入有效的数量');
return;
}
const existingProduct = products.find(p => p.barcode === scannedBarcode);
if (existingProduct) {
// 更新现有商品
setProducts(products.map(p =>
p.id === existingProduct.id
? { ...p, quantity: p.quantity + quantity }
: p
));
} else {
// 创建新商品
const newProduct: Product = {
id: (products.length + 1).toString(),
name: `商品-${scannedBarcode.slice(-4)}`,
barcode: scannedBarcode,
quantity: quantity,
location: '待分配',
category: '未分类'
};
setProducts([...products, newProduct]);
}
// 添加库存记录
const record: InventoryRecord = {
id: (inventoryRecords.length + 1).toString(),
productId: existingProduct?.id || (products.length + 1).toString(),
operation: '入库',
quantity: quantity,
timestamp: new Date().toLocaleString('zh-CN'),
operator: '当前用户'
};
setInventoryRecords([...inventoryRecords, record]);
Alert.alert('成功', '库存已更新');
setScannedBarcode('');
setNewQuantity('');
};
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.scanCard}>
<Text style={styles.sectionTitle}>扫描入库</Text>
<View style={styles.scanArea}>
<Text style={styles.scanIcon}>📷</Text>
<TextInput
style={styles.barcodeInput}
placeholder="扫描或输入条形码"
value={scannedBarcode}
onChangeText={setScannedBarcode}
/>
</View>
<TouchableOpacity
style={styles.scanButton}
onPress={handleScanBarcode}
>
<Text style={styles.scanButtonText}>扫描条码</Text>
</TouchableOpacity>
</View>
{/* 手动录入 */}
<View style={styles.manualCard}>
<Text style={styles.sectionTitle}>手动录入</Text>
<TextInput
style={styles.input}
placeholder="入库数量"
value={newQuantity}
onChangeText={setNewQuantity}
keyboardType="numeric"
/>
<TouchableOpacity
style={styles.entryButton}
onPress={handleManualEntry}
>
<Text style={styles.entryButtonText}>确认入库</Text>
</TouchableOpacity>
</View>
{/* 库存列表 */}
<View style={styles.inventoryCard}>
<Text style={styles.sectionTitle}>当前库存</Text>
{products.map(product => (
<View key={product.id} style={styles.productItem}>
<View style={styles.productInfo}>
<Text style={styles.productName}>{product.name}</Text>
<Text style={styles.productBarcode}>条码: {product.barcode}</Text>
<Text style={styles.productLocation}>位置: {product.location}</Text>
</View>
<View style={styles.stockInfo}>
<Text style={styles.stockQuantity}>{product.quantity}</Text>
<Text style={styles.stockUnit}>件</Text>
</View>
</View>
))}
</View>
{/* 库位信息 */}
<View style={styles.locationsCard}>
<Text style={styles.sectionTitle}>库位状态</Text>
{storageLocations.map(location => (
<View key={location.id} style={styles.locationItem}>
<View style={styles.locationInfo}>
<Text style={styles.locationCode}>{location.code}</Text>
<Text style={styles.locationCapacity}>
容量: {location.currentStock}/{location.capacity}
</Text>
</View>
<View style={styles.locationStatus}>
<View style={[
styles.statusIndicator,
{ backgroundColor: location.isAvailable ? '#10b981' : '#6b7280' }
]} />
<Text style={styles.statusText}>
{location.isAvailable ? '可用' : '已满'}
</Text>
</View>
</View>
))}
</View>
{/* 操作记录 */}
<View style={styles.recordsCard}>
<Text style={styles.sectionTitle}>操作记录</Text>
{inventoryRecords.slice(-5).reverse().map(record => {
const product = products.find(p => p.id === record.productId);
return (
<View key={record.id} style={styles.recordItem}>
<View style={styles.recordHeader}>
<Text style={styles.recordProduct}>{product?.name || '未知商品'}</Text>
<Text style={[
styles.recordOperation,
{ color: record.operation === '入库' ? '#10b981' : '#ef4444' }
]}>
{record.operation}
</Text>
</View>
<View style={styles.recordDetails}>
<Text style={styles.recordQuantity}>数量: {record.quantity}</Text>
<Text style={styles.recordTime}>{record.timestamp}</Text>
</View>
</View>
);
})}
</View>
{/* 使用说明 */}
<View style={styles.infoCard}>
<Text style={styles.sectionTitle}>使用说明</Text>
<Text style={styles.infoText}>• 扫描商品条码快速入库</Text>
<Text style={styles.infoText}>• 系统自动分配最优库位</Text>
<Text style={styles.infoText}>• 实时更新库存数据</Text>
<Text style={styles.infoText}>• 支持手动录入补充</Text>
</View>
</ScrollView>
{/* 底部导航 */}
<View style={styles.bottomNav}>
<TouchableOpacity style={styles.navItem}>
<Text style={styles.navIcon}>📱</Text>
<Text style={styles.navText}>扫描</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.navItem}>
<Text style={styles.navIcon}>📦</Text>
<Text style={styles.navText}>库存</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.navItem}>
<Text style={styles.navIcon}>📍</Text>
<Text style={styles.navText}>库位</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.navItem, styles.activeNavItem]}>
<Text style={styles.navIcon}>👤</Text>
<Text style={styles.navText}>我的</Text>
</TouchableOpacity>
</View>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f0f9ff',
},
header: {
flexDirection: 'column',
padding: 16,
backgroundColor: '#ffffff',
borderBottomWidth: 1,
borderBottomColor: '#bae6fd',
},
title: {
fontSize: 20,
fontWeight: 'bold',
color: '#0c4a6e',
marginBottom: 4,
},
subtitle: {
fontSize: 14,
color: '#0284c7',
},
content: {
flex: 1,
marginTop: 12,
},
scanCard: {
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: '#0c4a6e',
marginBottom: 12,
},
scanArea: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#f0f9ff',
borderRadius: 8,
paddingHorizontal: 12,
marginBottom: 12,
},
scanIcon: {
fontSize: 20,
color: '#64748b',
marginRight: 8,
},
barcodeInput: {
flex: 1,
paddingVertical: 12,
fontSize: 14,
color: '#0c4a6e',
},
scanButton: {
backgroundColor: '#0284c7',
paddingVertical: 14,
borderRadius: 8,
alignItems: 'center',
},
scanButtonText: {
color: '#ffffff',
fontSize: 16,
fontWeight: '600',
},
manualCard: {
backgroundColor: '#ffffff',
marginHorizontal: 16,
marginBottom: 12,
borderRadius: 12,
padding: 16,
elevation: 2,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
},
input: {
borderWidth: 1,
borderColor: '#bae6fd',
borderRadius: 8,
padding: 12,
fontSize: 14,
backgroundColor: '#f0f9ff',
marginBottom: 12,
},
entryButton: {
backgroundColor: '#10b981',
paddingVertical: 14,
borderRadius: 8,
alignItems: 'center',
},
entryButtonText: {
color: '#ffffff',
fontSize: 16,
fontWeight: '600',
},
inventoryCard: {
backgroundColor: '#ffffff',
marginHorizontal: 16,
marginBottom: 12,
borderRadius: 12,
padding: 16,
elevation: 2,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
},
productItem: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingVertical: 12,
borderBottomWidth: 1,
borderBottomColor: '#bae6fd',
},
productInfo: {
flex: 1,
},
productName: {
fontSize: 14,
fontWeight: '500',
color: '#0c4a6e',
marginBottom: 2,
},
productBarcode: {
fontSize: 12,
color: '#64748b',
marginBottom: 2,
},
productLocation: {
fontSize: 12,
color: '#64748b',
},
stockInfo: {
alignItems: 'center',
},
stockQuantity: {
fontSize: 18,
fontWeight: 'bold',
color: '#0284c7',
},
stockUnit: {
fontSize: 12,
color: '#64748b',
},
locationsCard: {
backgroundColor: '#ffffff',
marginHorizontal: 16,
marginBottom: 12,
borderRadius: 12,
padding: 16,
elevation: 2,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
},
locationItem: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingVertical: 12,
borderBottomWidth: 1,
borderBottomColor: '#bae6fd',
},
locationInfo: {
flex: 1,
},
locationCode: {
fontSize: 14,
fontWeight: '500',
color: '#0c4a6e',
marginBottom: 2,
},
locationCapacity: {
fontSize: 12,
color: '#64748b',
},
locationStatus: {
alignItems: 'center',
},
statusIndicator: {
width: 12,
height: 12,
borderRadius: 6,
marginBottom: 4,
},
recordsCard: {
backgroundColor: '#ffffff',
marginHorizontal: 16,
marginBottom: 12,
borderRadius: 12,
padding: 16,
elevation: 2,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
},
recordItem: {
paddingVertical: 12,
borderBottomWidth: 1,
borderBottomColor: '#bae6fd',
},
recordHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 4,
},
recordProduct: {
fontSize: 14,
fontWeight: '500',
color: '#0c4a6e',
},
recordOperation: {
fontSize: 12,
fontWeight: '500',
},
recordDetails: {
flexDirection: 'row',
justifyContent: 'space-between',
},
recordQuantity: {
fontSize: 12,
color: '#64748b',
},
recordTime: {
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: '#bae6fd',
paddingVertical: 12,
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
},
navItem: {
alignItems: 'center',
flex: 1,
},
activeNavItem: {
paddingTop: 4,
borderTopWidth: 2,
borderTopColor: '#0284c7',
},
navIcon: {
fontSize: 20,
color: '#94a3b8',
marginBottom: 4,
},
activeNavIcon: {
color: '#0284c7',
},
navText: {
fontSize: 12,
color: '#94a3b8',
},
activeNavText: {
color: '#0284c7',
fontWeight: '500',
},
});
export default WarehouseManagementApp;

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

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

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

本项目基于React Native开发跨平台仓库管理应用,支持iOS、Android和鸿蒙系统。采用函数式组件架构,通过TypeScript类型系统定义商品(Product)、库存记录(InventoryRecord)和库位(StorageLocation)等核心数据结构。使用Hooks管理状态,实现商品入库、条形码扫描、库存记录等核心功能。应用采用模块化设计,包含数据验证、用户交互反馈等优化措施,同时针对鸿蒙平台进行适配,确保跨平台兼容性。技术栈包括React Native、TypeScript和Flexbox布局,提供响应式UI和良好的开发体验。
欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。
更多推荐
所有评论(0)