发货-创建发货单
FBA 发货单创建功能详细帮助文档
1. 功能概述
本文档详细介绍 FBA 发货单创建功能的实现,包括前端组件结构、API 调用、后端实现、数据模型和业务逻辑流程。该功能位于 wimoor666\wimoor-ui\src\views\erp\shipv2\shipment_add\create\ 目录下,是 FBA 发货流程的起点,用于创建新的发货计划。
2. 前端组件结构
2.1 组件层级
index.vue (容器组件)
└── Fromlist (核心表单组件,即 ./components/form.vue)
├── 基本信息表单
│ ├── 计划编码
│ ├── 发货店铺
│ ├── 发货仓库
│ ├── 收货国家
│ ├── 发货地址
│ ├── 备注
│ ├── 运输方式
│ ├── 物流方式
│ └── 单据类型
├── 商品管理
│ ├── 添加商品
│ ├── 导入商品
│ ├── 打印标签
│ ├── 清空列表
│ └── 商品列表
└── 对话框
├── 地址编辑对话框
├── 商品选择对话框
├── 导入数据对话框
└── 其它信息设置对话框
2.2 核心文件
| 文件路径 | 功能描述 |
|---|---|
wimoor666\wimoor-ui\src\views\erp\shipv2\shipment_add\create\index.vue |
容器组件,加载核心表单 |
wimoor666\wimoor-ui\src\views\erp\shipv2\shipment_add\create\components\form.vue |
核心表单组件,包含完整的发货单创建功能 |
wimoor666\wimoor-ui\src\views\erp\shipv2\shipment_add\create\components\mateiralDialog.vue |
商品选择对话框 |
wimoor666\wimoor-ui\src\views\amazon\address\components\editdialog.vue |
地址编辑对话框 |
3. API 调用分析
3.1 前端 API 调用
| API 方法 | 用途 | 参数 | 来源文件 |
|---|---|---|---|
groupApi.getAmazonGroup() |
获取店铺列表 | 无 | groupApi.js |
marketApi.getMarketByGroup({groupid}) |
获取国家列表 | {groupid} |
marketApi.js |
warehouseApi.getWarehouseUseable() |
获取仓库列表 | 无 | [warehouseApi.js] |
shipaddressApi.getAddressByid({addressid, groupid}) |
获取地址列表 | {addressid, groupid} |
[shipaddressApi.js] |
serialnumberApi.getSerialNumber({ftype, isfind}) |
获取序列号 | {ftype: 'SF', isfind: 'true'} |
[serialnumberApi.js] |
transportationApi.getTransTypeAll() |
获取物流类型列表 | 无 | [transportationApi.js] |
shipmentplanApi.saveInboundPlan(formData) |
保存入库计划 | formData |
[shipmentplanApi.js] |
profitParamApi.getInplaceList({country}) |
获取入库地点列表 | {country: 'US'} |
[profitParamApi.js] |
3.2 后端控制器实现
| 控制器方法 | 用途 | 路径 | 来源文件 |
|---|---|---|---|
ShipInboundPlanV2Controller.saveInboundPlanAction() |
保存入库计划 | /api/v2/shipInboundPlan/saveInboundPlan |
ShipInboundPlanV2Controller.java |
AmazonAuthorityController.getAmazonGroupAction() |
获取店铺列表 | /amazon/api/v1/amzauthority/getAmazonGroup |
AmazonAuthorityController.java |
AmazonAuthorityController.getMarketByGroupAction() |
获取国家列表 | /amazon/api/v1/amzauthority/getMarketByGroup |
AmazonAuthorityController.java |
4. 后端数据模型
4.1 核心实体类
| 实体类 | 描述 | 表名 | 来源文件 |
|---|---|---|---|
ShipInboundPlan |
发货计划 | t_erp_ship_v2_inboundplan |
ShipInboundPlan.java |
ShipInboundItem |
发货计划商品 | t_erp_ship_v2_inbounditem |
ShipInboundItem.java |
ShipInboundShipment |
货件 | t_erp_ship_v2_inboundshipment |
ShipInboundShipment.java |
ShipInboundBox |
箱子信息 | t_erp_ship_v2_inboundbox |
ShipInboundBox.java |
4.2 实体类关系
ShipInboundPlan (1) ──── (N) ShipInboundItem
ShipInboundPlan (1) ──── (N) ShipInboundShipment
ShipInboundShipment (1) ──── (N) ShipInboundBox
5. 业务逻辑流程
5.1 初始化流程
- 组件加载:
onMounted钩子函数触发初始化 - 初始化查询数据:
initQueryData()从路由参数中获取初始化数据 - 加载计划名称:
loadShipName()生成计划名称 - 加载计划编码:
loadNumber()获取序列号作为计划编码 - 加载店铺列表:
getGroupData("init")获取店铺列表并设置默认值 - 加载仓库列表:
getWarehouseData()获取仓库列表并设置默认值 - 加载物流方式:
getTranList()获取物流方式列表 - 加载入库地点:
loadInplace()获取入库地点列表
5.2 表单填写流程
-
基本信息填写:
- 选择发货店铺 → 触发
groupChange()→ 加载对应市场列表和地址列表 - 选择发货仓库 → 触发
warehouseChange()→ 更新仓库信息 - 选择收货国家 → 触发
marketChange()→ 更新国家信息并验证 SKU - 选择发货地址 → 触发
radioChange()→ 更新地址信息 - 填写备注、运输方式、物流方式、单据类型
- 选择发货店铺 → 触发
-
商品管理:
- 添加商品:点击"添加商品"按钮 → 打开商品选择对话框 → 选择商品 → 触发
getdata()→ 添加商品到列表 - 导入商品:点击"导入"按钮 → 打开导入数据对话框 → 上传文件 → 处理导入数据
- 打印标签:点击"打印当前产品标签"按钮 → 打印商品标签
- 清空列表:点击"清空列表"按钮 → 清空商品列表
- 删除商品:点击商品行的"删除"按钮 → 从列表中移除商品
- 其他维护:点击"其它维护"按钮 → 打开其它信息设置对话框 → 设置预备信息处理人、贴标人、保质期
- 添加商品:点击"添加商品"按钮 → 打开商品选择对话框 → 选择商品 → 触发
5.3 提交流程
-
准备数据:
- 收集表单数据
- 处理商品列表数据
- 设置计划项目列表
-
提交计划:
- 点击"提交"按钮 → 触发
submitPlan() - 显示加载状态
- 调用
shipmentplanApi.saveInboundPlan(state.formData)提交数据 - 处理响应:显示成功消息 → 跳转到新货件详情页面
- 点击"提交"按钮 → 触发
-
取消操作:
- 点击"取消"按钮 → 触发
cancelPlan()→ 清空商品列表并触发取消事件
- 点击"取消"按钮 → 触发
6. 核心代码分析
6.1 前端核心代码
6.1.1 提交计划
function submitPlan() {
var itemlist = [];
state.formData.groupid = state.formData.groupid;
state.formData.planmarketplaceid = state.queryData.marketplaceid;
state.formData.planid = state.queryData.planid;
state.formData.batchnumber = state.queryData.batchnumber;
state.formData.issplit = state.queryData.issplit;
state.productlist.forEach(function(item) {
var row = item;
row.QuantityShipped = item.quantity;
if (state.formData.arecasesrequired) {
row.boxnum = item.boxnum;
}
row.materialid = item.id;
row.sellersku = item.sku;
row.opttime = null;
itemlist.push(row);
});
state.formData.planitemlist = itemlist;
state.submitloading = true;
if (props.isappend) {
state.submitloading = false;
emit("change", state);
} else {
if (!state.formData.sourceAddress) {
ElMessage.error('地址信息未选中,请刷新重试!');
return;
}
shipmentplanApi.saveInboundPlan(state.formData).then((res) => {
ElMessage.success('已提交成功!');
state.submitloading = false;
router.push({
path: '/newshipmentdetails',
query: {
id: res.data,
title: "新货件详情",
replace: true,
path: '/newshipmentdetails',
}
})
}).catch(error => {
state.submitloading = false;
})
}
}
6.1.2 获取店铺列表
function getGroupData(type) {
groupApi.getAmazonGroup().then((res) => {
state.groupList = res.data;
if (res.data && res.data.length > 0) {
if (!state.formData.groupid || state.formData.groupid == "") {
if (type == 'init' && state.queryData.groupid) {
state.formData.groupid = state.queryData.groupid;
} else {
state.formData.groupid = res.data[0].id;
}
}
getMarketData(state.formData.groupid, type);
loadAddress();
loadPlanData();
}
})
}
6.2 后端核心代码
6.2.1 保存入库计划
@ApiOperation(value = "提交发货计划")
@PostMapping("/saveInboundPlan")
@SystemControllerLog("新增")
@Transactional
public Result<String> saveInboundPlanAction(@ApiParam("发货计划")@RequestBody ShipInboundPlan inplan){
UserInfo user=UserInfoContext.get();
inplan.setCreatetime(new Date());
inplan.setCreator(user.getId());
inplan.setOperator(user.getId());
inplan.setOpttime(new Date());
inplan.setShopid(user.getCompanyid());
inplan.setAuditstatus(1);
inplan.setInvstatus(0);
try {
inplan.setNumber(serialNumService.readSerialNumber(user.getCompanyid(), "SF"));
} catch (Exception e) {
e.printStackTrace();
try {
inplan.setNumber(serialNumService.readSerialNumber(user.getCompanyid(), "SF"));
} catch (Exception e1) {
e1.printStackTrace();
throw new BizException("编码获取失败,请联系管理员");
}
}
try {
if(inplan.getInvtype()==null) {
inplan.setInvtype(0);
}
shipInboundPlanV2Service.saveShipInboundPlan(inplan);
shipInboundV2ShipmentRecordService.saveRecord(inplan);
if(StrUtil.isNotBlank(inplan.getBatchnumber()) ) {
iAmzProductSalesPlanShipItemService.moveBatch(user.getCompanyid(),inplan.getBatchnumber());
}
return Result.success(inplan.getId());
}catch(FeignException e) {
throw new BizException("提交失败" +e.getMessage());
}catch(Exception e) {
throw new BizException("提交失败" +e.getMessage());
}
}
6.2.2 获取店铺列表
@ApiOperation(value = "获取店铺")
@GetMapping("/getAmazonGroup")
public Result<List<AmazonGroup>> getAmazonGroupAction() {
UserInfo userinfo = UserInfoContext.get();
List<AmazonGroup> result = iAmazonGroupService.getGroupByUser(userinfo);
return Result.success(result);
}
7. 技术栈
| 类别 | 技术/框架 | 版本 | 用途 |
|---|---|---|---|
| 前端 | Vue | 3.x | 前端框架 |
| 前端 | Element Plus | 最新版 | UI 组件库 |
| 前端 | Icon Park | 最新版 | 图标库 |
| 前端 | Axios | 最新版 | HTTP 客户端 |
| 后端 | Spring Boot | 最新版 | 后端框架 |
| 后端 | MyBatis Plus | 最新版 | ORM 框架 |
| 后端 | Swagger | 最新版 | API 文档 |
| 数据库 | MySQL | 最新版 | 数据库 |
8. 输入输出示例
8.1 输入示例
// 表单数据示例
const formData = {
name: "PLN(1/22/2026 14:30)-1",
number: "SF202601220001",
groupid: "group1",
warehouseid: "warehouse1",
marketplaceid: "US",
amazonauthid: "auth1",
sourceAddress: "address1",
remark: "测试发货计划",
transtyle: "SP",
transtype: "trans1",
invtype: 0,
planitemlist: [
{
sku: "SKU001",
msku: "MSKU001",
quantity: 10,
labelOwner: "SELLER",
prepOwner: "SELLER",
materialid: "material1",
sellersku: "SKU001"
},
{
sku: "SKU002",
msku: "MSKU002",
quantity: 5,
labelOwner: "SELLER",
prepOwner: "SELLER",
materialid: "material2",
sellersku: "SKU002"
}
]
};
// 调用API
const { data } = await shipmentplanApi.saveInboundPlan(formData);
8.2 输出示例
// 成功响应
{
"code": 0,
"msg": "",
"data": "plan123456"
}
// 失败响应
{
"code": 500,
"msg": "提交失败:编码获取失败,请联系管理员",
"data": null
}
9. 业务流程图
flowchart TD
A[进入添加发货单页面] --> B[初始化数据]
B --> C[填写基本信息]
C --> D[选择发货店铺]
D --> E[选择发货仓库]
E --> F[选择收货国家]
F --> G[选择发货地址]
G --> H[填写其他信息]
H --> I[添加商品]
I --> J[提交发货计划]
J --> K[后端处理]
K --> L[返回计划ID]
L --> M[跳转到新货件详情页面]
subgraph 商品管理
I1[添加商品]
I2[导入商品]
I3[打印标签]
I4[清空列表]
I5[删除商品]
I6[其他维护]
end
I --> I1
I --> I2
I --> I3
I --> I4
I --> I5
I --> I6
10. 代码优化建议
10.1 前端优化
- 错误处理增强:添加更详细的错误处理和用户提示,特别是在 API 调用失败时
- 性能优化:对于大量商品的列表,考虑使用虚拟滚动
- 代码组织:将复杂的业务逻辑拆分为更小的函数,提高代码可读性
- 表单验证:添加更严格的表单验证,确保数据完整性
- 用户体验:添加加载状态和进度指示,提升用户体验
- 代码复用:提取重复的代码为公共函数或组件
10.2 后端优化
- 事务管理:确保
saveInboundPlan方法中的事务处理更加健壮 - 参数验证:增强请求参数的验证,确保数据完整性
- 错误处理:提供更详细的错误信息,便于前端处理
- 性能优化:对于频繁查询的数据,考虑添加缓存
- 日志记录:添加详细的日志记录,便于问题排查
11. 常见问题及解决方案
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 地址信息未选中 | 地址列表加载失败或用户未选择 | 刷新页面重新加载地址列表,确保选择一个有效的发货地址 |
| 编码获取失败 | 序列号生成服务异常 | 检查序列号服务状态,或联系管理员 |
| 提交失败 | 后端处理异常 | 检查网络连接,查看控制台错误信息,或联系管理员 |
| 商品数量超过库存 | 发货数量大于可用库存 | 减少发货数量,确保不超过可用库存 |
| SKU 验证失败 | SKU 不存在或无效 | 检查 SKU 是否正确,确保 SKU 在系统中存在 |
12. 结论
FBA 发货单创建功能是一个功能完整、设计合理的业务组件,为用户提供了直观、高效的发货计划创建界面。通过前端与后端的紧密协作,实现了从基本信息填写到商品管理再到计划提交的完整流程。
该功能的实现展示了现代前端开发的最佳实践,包括:
- 使用 Vue 3 Composition API 进行状态管理
- 与后端 API 的高效交互
- 响应式 UI 设计
- 良好的用户体验
- 模块化的代码组织
同时,后端实现也体现了 Spring Boot 框架的优势,包括:
- 清晰的控制器设计
- 灵活的服务层架构
- 高效的数据处理
- 完善的事务管理
总体而言,FBA 发货单创建功能是一个设计精良、功能完善的业务组件,为 FBA 发货流程提供了重要的起点,帮助用户快速、准确地创建发货计划,提高了物流管理的效率。