Commit 23d77852 by yubin

Merge remote-tracking branch 'origin/master'

parents 1976459d ccd1dfaa
......@@ -18,29 +18,6 @@
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
<div class="el-upload__tip text-center" slot="tip">
<div
class="order-type-radio"
v-if="showTrdcCheckbox && dict.type.inbound_outbound_type.length"
style="margin-bottom: 8px; text-align: left; padding-left: 20px;"
>
<el-radio-group v-model="upload.orderType">
<el-radio
v-for="item in dict.type.inbound_outbound_type"
:key="item.value"
:label="item.value"
style="margin-right: 20px;"
>
{{ item.label }}
</el-radio>
</el-radio-group>
</div>
<!-- 加载中提示 -->
<div
v-else-if="showTrdcCheckbox && !dict.type.inbound_outbound_type.length"
style="margin-bottom: 8px; text-align: left; padding-left: 20px;"
>
<i class="el-icon-loading" style="font-size: 14px;"></i> 加载入库类型...
</div>
<!-- <div class="el-upload__tip" slot="tip">
<el-checkbox v-model="upload.updateSupport" />是否更新已经存在的数据
</div> -->
......@@ -189,7 +166,6 @@ export default {
},
// 提交上传文件
submitFileForm() {
this.upload.isUploading = true
const file = this.$refs.upload.uploadFiles
if (
!file ||
......@@ -200,11 +176,13 @@ export default {
this.$modal.msgError("请选择后缀为 “xls”或“xlsx”的文件。")
return
}
// 校验orderType(如果是必传项)
if (this.showTrdcCheckbox && this.orderTypeRequired && !this.upload.orderType) {
this.$modal.msgError("请选择入库/出库类型!")
return
}
this.upload.isUploading = true
// 提交前通知父组件
this.$emit("orderTypeChange", this.upload.orderType)
// 提交上传
......
......@@ -13,6 +13,16 @@
>新增</el-button>
<el-button
type="primary"
plain
icon="el-icon-success"
size="medium"
:disabled="multiple"
@click="handleConfirm()"
v-hasPermi="['inventory:inbound:add']"
>确认入库</el-button>
<el-button
type="success"
plain
icon="el-icon-edit"
......@@ -101,7 +111,7 @@
<el-form-item label="入库类型" prop="orderTypeId">
<el-select v-model="queryParams.orderTypeId" placeholder="请选择入库类型" clearable>
<el-option
v-for="dict in dict.type.inbound_outbound_type"
v-for="dict in dict.type.inbound_type"
:key="dict.value"
:label="dict.label"
:value="dict.value"
......@@ -164,10 +174,10 @@
<el-table-column label="入库类型" align="center" prop="orderTypeId" show-overflow-tooltip min-width="200">
<template slot-scope="scope">
<el-tag
:type="getDictTagClass('inbound_outbound_type', scope.row.orderTypeId)"
:type="getDictTagClass('inbound_type', scope.row.orderTypeId)"
size="small"
>
{{ getDictLabel('inbound_outbound_type', scope.row.orderTypeId) }}
{{ getDictLabel('inbound_type', scope.row.orderTypeId) }}
</el-tag>
</template>
</el-table-column>
......@@ -272,7 +282,7 @@
<el-form-item label="入库类型" prop="orderTypeId">
<el-select v-model="form.orderTypeId" placeholder="请选择入库类型" clearable>
<el-option
v-for="dict in dict.type.inbound_outbound_type"
v-for="dict in dict.type.inbound_type"
:key="dict.value"
:label="dict.label"
:value="dict.value"
......@@ -377,7 +387,7 @@
<el-descriptions-item label="批次号">{{ detailForm.batchId || '-' }}</el-descriptions-item>
<el-descriptions-item label="货主">{{ detailForm.ownerName || detailForm.ownerId || '-' }}</el-descriptions-item>
<el-descriptions-item label="入库类型">
{{ getDictLabel('inbound_outbound_type', detailForm.orderTypeId) }}
{{ getDictLabel('inbound_type', detailForm.orderTypeId) }}
</el-descriptions-item>
<el-descriptions-item label="订单类型">
{{ getDictLabel('order_type', detailForm.orderType) }}
......@@ -470,7 +480,7 @@ import {
updateInbound
} from "@/api/inventory/inbound"
import { listInbound_itemsAndMname } from "@/api/inventory/inbound_items"
import { batchAddInventory } from "@/api/inventory/inventory"
import { batchAddInventory} from "@/api/inventory/inventory"
import InboundItems from "@/views/inventory/inbound_items/index.vue"
import PageTitle from "@/components/PageTitle"
import PageWrapperSearch from "@/components/Search/PageWrapperSearch"
......@@ -482,7 +492,7 @@ import LocationSelector from "@/views/compononents/LocationSelector.vue"
export default {
name: "Inbound",
dicts: ['inbound_outbound_type', 'order_type', 'inbound_status'],
dicts: ['inbound_outbound_type', 'order_type', 'inbound_status', 'inbound_type'],
components: {
InboundItems,
PageTitle,
......@@ -930,53 +940,143 @@ export default {
},
/**
* 确认入库操作
* @param {Object} row 选中行数据
* 确认入库操作(支持单选/多选)
* @param {Object} row 选中行数据(单选时有值,多选时为null/undefined)
*/
async handleConfirm(row) {
// 1. 确定要处理的入库单ID列表(单选取row.id,多选取已选中的ids)
let handleIds = [];
if (row) {
// 单选场景:仅处理当前行
handleIds = [row.id];
} else {
// 多选场景:取表格选中的ids(需确保this.ids是已选中的入库单ID数组)
handleIds = this.ids;
if (handleIds.length === 0) {
return this.$message.warning('请选择需要入库的单据!');
}
}
// 2. 二次确认(批量时提示选中的数量)
try {
await this.$confirm('确认要入库吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
});
await this.$confirm(
`确认要对选中的 ${handleIds.length} 条入库单执行入库操作吗?`,
'批量入库提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
);
} catch (error) {
// 取消确认时直接返回
return;
}
// ✨ 关键:打开全局遮罩层(Element UI 全局loading)
const loadingInstance = this.$loading({
lock: true, // 锁定屏幕,禁止滚动/点击
text: '正在入库,请稍候...', // 加载提示文字
spinner: 'el-icon-loading', // 加载图标
background: 'rgba(0, 0, 0, 0.7)' // 遮罩层背景(半透明黑色)
});
// 3. 批量处理核心逻辑(加loading防止重复操作)
let successCount = 0; // 成功数量
let failList = []; // 失败的单据记录
// 查询入库明细
const queryForm = { pageNum: 1, pageSize: 9999, inboundOrderId: row.id };
const res = await listInbound_itemsAndMname(queryForm);
const items = res.rows || [];
try {
// 遍历每个入库单ID,逐行处理
for (const inboundId of handleIds) {
try {
// 新增:前置校验入库单ID是否有效
if (!inboundId || inboundId === null || inboundId === undefined) {
failList.push({ id: inboundId, reason: '入库单ID为空,跳过处理' });
console.warn('入库单ID为空,跳过:', inboundId);
continue;
}
// 3.1 查询当前入库单的基础信息(根据ID查单条入库单)
// 需补充:getInboundById 接口(根据id查询入库单详情)
const currentRow = await getInbound(inboundId);
if (!currentRow) {
failList.push({ id: inboundId, reason: '入库单不存在' });
continue;
}
// 跳过已入库的单据
if (currentRow.data.orderStatus === 2) {
failList.push({ id: inboundId, reason: '该单据已完成入库,无需重复操作' });
continue;
}
// 新增:校验currentRow.id是否存在(防止后端返回空ID的对象)
if (!currentRow.data.id) {
failList.push({ id: inboundId, reason: '入库单详情ID为空' });
continue;
}
// 3.2 查询当前入库单的明细
const queryForm = {
pageNum: 1,
pageSize: 9999,
inboundOrderId: currentRow.data.id
};
console.log(`查询入库单${inboundId}的明细,参数:`, queryForm);
const res = await listInbound_itemsAndMname(queryForm);
const items = res.rows || [];
if (items.length === 0) {
failList.push({ id: inboundId, reason: '暂无入库明细数据' });
continue;
}
// 3.3 组装库存数据
const inventoryItems = items.map(item => ({
...item,
inventoryType: currentRow.data.orderTypeId,
warehousesId: item.warehouseId,
inventoryStatus: 1,
ownerId: currentRow.data.ownerId,
quantity: item.actualQuantity,
unitWeight: item.unitWeight,
isUsed: 1,
unitPrice: item.unitPrice
}));
// 3.4 批量添加库存
await batchAddInventory(inventoryItems);
// 3.5 更新当前入库单状态
currentRow.data.orderStatus = 2;
await updateInbound(currentRow.data);
successCount++; // 成功计数+1
} catch (error) {
// 单个单据失败不中断整体流程,记录失败原因
failList.push({
id: inboundId,
reason: error.msg || error.message || '处理失败'
});
console.error(`入库单${inboundId}处理失败:`, error);
}
}
if (items.length === 0) {
return this.$message.warning('暂无入库明细数据,无法确认入库');
// 4. 批量处理完成后,统一提示结果
let message = '';
if (successCount > 0) {
message += `成功入库 ${successCount} 条单据;`;
}
if (failList.length > 0) {
message += `失败 ${failList.length} 条单据:${failList.map(item => `ID[${item.id}](${item.reason})`).join(';')}`;
}
this.$message[successCount > 0 ? 'success' : 'error'](message);
// 组装库存数据
const inventoryItems = items.map(item => ({
...item,
inventoryType: row.orderTypeId,
warehousesId: item.warehouseId,
inventoryStatus: 1,
ownerId: row.ownerId,
quantity: item.actualQuantity,
unitWeight: item.unitWeight,
isUsed: 1,
unitPrice: item.unitPrice
}));
// 批量添加库存
await batchAddInventory(inventoryItems);
// 更新入库单状态
row.orderStatus = 2;
await updateInbound(row);
this.$message.success('确认入库成功');
// 5. 刷新列表
this.getList();
} catch (error) {
if (error !== 'cancel') {
this.$message.error('确认入库失败:' + (error.msg || error.message || '网络异常'));
console.error('确认入库失败:', error);
}
// 全局异常捕获(如网络中断等)
this.$message.error('批量入库操作异常:' + (error.msg || error.message || '系统异常'));
console.error('批量入库全局异常:', error);
} finally {
// ✨ 关键:关闭全局遮罩层(无论成功/失败都关闭)
loadingInstance.close();
// 清空选中的ids(多选后重置)
this.ids = [];
}
},
......
......@@ -293,7 +293,7 @@ public class InboundOrdersController extends BaseController
}
// 3. 通用导入逻辑(抽取重复代码,避免冗余)
String message = handleImport(getVOClassByOrderType(orderType), file, headerList, updateSupport, getUsername(), orderType);
String message = handleImport(InboundTemplateVO.class, file, headerList, updateSupport, getUsername(), orderType);
return success(message);
}
......@@ -309,6 +309,7 @@ public class InboundOrdersController extends BaseController
}
}
private static final Map<Class<?>, List<String>> REQUIRED_HEADS_CACHE = new ConcurrentHashMap<>();
/**
* 通用导入逻辑(泛型适配不同VO)
*/
......
......@@ -68,11 +68,11 @@ public class InboundTemplateVO extends BaseEntity {
private String warehouseId;
/** 备注 */
// @Excel(name = "备注")
@Excel(name = "备注")
private String remark;
/** 库位ID 检索条件 */
@Excel(name = "收货库位")
// @Excel(name = "收货库位")
private String remark2;
/** 标签颜色 字典,检索条件 */
......
......@@ -297,7 +297,7 @@ public class InboundOrdersServiceImpl implements IInboundOrdersService
InboundOrders mainDO = null;
List<InboundOrderItems> itemDOList = new ArrayList<>();
List<Inventory> inventoryList = new ArrayList<>();
// List<Inventory> inventoryList = new ArrayList<>();
try {
// 4.1 处理主表(每个入库单号只处理一次主表)
T firstVO = voList.get(0); // 取第一条VO的主表信息
......@@ -336,6 +336,7 @@ public class InboundOrdersServiceImpl implements IInboundOrdersService
"plannedPackages", "materialUnit", "materialRemark"); // 排除子表字段
// 填充主表必填字段
mainDO.setId(UUID.randomUUID().toString());
mainDO.setOrderTypeId(mainDO.getOrderType());
mainDO.setInboundDate(now);
mainDO.setOrderId(orderId);
mainDO.setCreateBy(operId);
......@@ -343,13 +344,13 @@ public class InboundOrdersServiceImpl implements IInboundOrdersService
mainDO.setCreateUserCode(operId);
mainDO.setIsImport(1);
mainDO.setIsUsed(1L);
mainDO.setOrderTypeId(Optional.ofNullable(orderType).map(String::valueOf).orElse(""));
// mainDO.setOrderTypeId(Optional.ofNullable(orderType).map(String::valueOf).orElse(""));
// 设置默认值
if (mainDO.getSortNo() == null) {
mainDO.setSortNo(0L);
}
if (mainDO.getOrderStatus() == null) {
mainDO.setOrderStatus(2L); // 默认草稿状态
mainDO.setOrderStatus(1L); // 默认草稿状态
}
// ========== 货主查询(反射获取ownerId) ==========
......@@ -488,21 +489,21 @@ public class InboundOrdersServiceImpl implements IInboundOrdersService
log.error("导入明细失败-入库单【{}】-SAP【{}】,实发数量和实际件数为空", orderId, sapNo, e);
throw new ServiceException("实发数量和实际件数为空");
}
Inventory inventoryDO = new Inventory(
Long.parseLong(mainDO.getOrderTypeId()),
orderId,
itemDO.getMaterialId(),
batchId,
itemDO.getWarehouseId(),
itemDO.getLocationId(),
mainDO.getOwnerId(),
itemDO.getActualQuantity() == null ? 0L : itemDO.getActualQuantity(),
unitWeight,
1L,
itemDO.getUnitPrice()
);
inventoryList.add(inventoryDO);
//
// Inventory inventoryDO = new Inventory(
// Long.parseLong(mainDO.getOrderTypeId()),
// orderId,
// itemDO.getMaterialId(),
// batchId,
// itemDO.getWarehouseId(),
// itemDO.getLocationId(),
// mainDO.getOwnerId(),
// itemDO.getActualQuantity() == null ? 0L : itemDO.getActualQuantity(),
// unitWeight,
// 1L,
// itemDO.getUnitPrice()
// );
// inventoryList.add(inventoryDO);
} catch (Exception e) {
// 单个明细失败:仅统计,不影响整单
totalItemFailure++;
......@@ -525,8 +526,8 @@ public class InboundOrdersServiceImpl implements IInboundOrdersService
// 4.3 批量插入明细
if (!CollectionUtils.isEmpty(itemDOList)) {
int itemSuccess = inboundOrdersMapper.batchInboundOrderItems(itemDOList);
int inventorySuccess = iInventoryService.insertInventoryList(inventoryList);
if (itemSuccess != itemDOList.size() && itemSuccess != inventorySuccess) {
// int inventorySuccess = iInventoryService.insertInventoryList(inventoryList);
if (itemSuccess != itemDOList.size()) {
// 批量插入部分失败,主动回滚事务
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
throw new ServiceException("明细批量插入失败,成功" + itemSuccess + "条,总" + itemDOList.size() + "条");
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论