Commit cbf9128a by 杨子

feat(入库/出库): 新增入库/出库管理模块

refactor(库存管理): 优化库存管理界面显示和交互

style(组件): 调整多个组件样式和布局

fix(出库单): 修正出库单详情显示问题

perf(对话框): 优化对话框性能和用户体验

docs(注释): 更新部分代码注释和文档
parent 26e22007
import request from '@/utils/request'
// 查询入库单详情列表
export function listWmsInboundOrderItemDetail(query) {
return request({
url: '/ware/wmsInboundOrderItemDetail/list',
method: 'get',
params: query
})
}
// 查询入库单详情详细
export function getWmsInboundOrderItemDetail(itemDetailId) {
return request({
url: '/ware/wmsInboundOrderItemDetail/' + itemDetailId,
method: 'get'
})
}
// 新增入库单详情
export function addWmsInboundOrderItemDetail(data) {
return request({
url: '/ware/wmsInboundOrderItemDetail',
method: 'post',
data: data
})
}
// 修改入库单详情
export function updateWmsInboundOrderItemDetail(data) {
return request({
url: '/ware/wmsInboundOrderItemDetail',
method: 'put',
data: data
})
}
// 删除入库单详情
export function delWmsInboundOrderItemDetail(itemDetailId) {
return request({
url: '/ware/wmsInboundOrderItemDetail/' + itemDetailId,
method: 'delete'
})
}
......@@ -2,7 +2,7 @@
<el-dialog
:title="title"
v-model="dialogVisible"
width="1200px"
width="1500px"
append-to-body
>
<el-form :model="queryParams" ref="queryRef" :inline="true" label-width="68px">
......@@ -28,15 +28,14 @@
</el-form-item>
</el-form>
<el-table v-loading="loading" :data="wmsInventoryFlowList">
<el-table-column label="流水ID" align="center" prop="flowId" />
<el-table v-loading="loading" :data="wmsInventoryFlowList" height="600px">
<el-table-column label="库存ID" align="center" prop="inventoryId" />
<el-table-column label="物资ID" align="center" prop="materialId" />
<el-table-column label="仓库ID" align="center" prop="warehouseId" />
<el-table-column label="货架ID" align="center" prop="locationId" />
<el-table-column label="流水类型" align="center" prop="flowType" />
<el-table-column label="流水单号" align="center" prop="flowCode" />
<el-table-column label="批次号" align="center" prop="batchNo" />
<el-table-column label="流水单号" align="center" prop="flowCode" width="150" />
<el-table-column label="批次号" align="center" prop="batchNo" width="150" />
<el-table-column label="变动前数量" align="center" prop="quantityBefore" />
<el-table-column label="变动数量" align="center" prop="quantityChange" />
<el-table-column label="变动后数量" align="center" prop="quantityAfter" />
......
......@@ -25,13 +25,13 @@
<el-table v-loading="loading" :data="inventoryList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="物资编码" align="center" prop="materialCode" />
<el-table-column label="物资名称" align="center" prop="materialName" />
<el-table-column label="规格型号" align="center" prop="specification" />
<el-table-column label="物资ID" align="center" prop="materialId" />
<!-- <el-table-column label="仓库ID" align="center" prop="warehouseId" /> -->
<!-- <el-table-column label="库区ID" align="center" prop="areaId" /> -->
<el-table-column label="货架ID" align="center" prop="locationId" />
<el-table-column label="物资编码" align="center" prop="material.materialCode" />
<el-table-column label="物资名称" align="center" prop="material.materialName" />
<el-table-column label="规格型号" align="center" prop="material.specification" />
<el-table-column label="计量单位" align="center" prop="material.unit" />
<el-table-column label="仓库" align="center" prop="warehouse.warehouseName" />
<el-table-column label="库区" align="center" prop="area.areaName" />
<el-table-column label="货架" align="center" prop="location.locationName" />
<el-table-column label="批次号" align="center" prop="batchNo" width="180" />
<el-table-column label="生产日期" align="center" prop="productionDate" width="180">
<template #default="scope">
......@@ -46,9 +46,8 @@
<el-table-column label="库存数量" align="center" prop="quantity" />
<el-table-column label="可用数量" align="center" prop="availableQuantity" />
<el-table-column label="锁定数量" align="center" prop="lockedQuantity" />
<el-table-column label="单位成本" align="center" prop="unitCost" />
<el-table-column label="入库单价" align="center" prop="unitCost" />
<el-table-column label="总成本" align="center" prop="totalCost" />
<!-- <el-table-column label="质量状态" align="center" prop="qualityStatus" /> -->
<el-table-column label="库存状态" align="center" prop="inventoryStatus">
<template #default="scope">
<dict-tag :options="inventory_status" :value="scope.row.inventoryStatus" />
......
<template>
<div class="location-cascader">
<el-form :model="form" label-width="80px">
<el-form-item label="仓库" v-if="!modelValue.warehouseId">
<el-select v-model="form.warehouseId" placeholder="请选择仓库" @change="handleWarehouseChange">
<el-form :model="form" label-width="50px">
<el-form-item label="仓库">
<el-select v-model="form.warehouseId" placeholder="请选择仓库" @change="handleWarehouseChange" size="small">
<el-option
v-for="warehouse in warehouseList"
:key="warehouse.warehouseId"
......@@ -13,7 +13,7 @@
</el-form-item>
<el-form-item label="库区">
<el-select v-model="form.areaId" placeholder="请选择库区" @change="handleAreaChange" :disabled="!form.warehouseId">
<el-select v-model="form.areaId" placeholder="请选择库区" @change="handleAreaChange" :disabled="!form.warehouseId" size="small">
<el-option
v-for="area in areaList"
:key="area.areaId"
......@@ -24,7 +24,7 @@
</el-form-item>
<el-form-item label="货架">
<el-select v-model="form.locationId" placeholder="请选择货架" :disabled="!form.areaId">
<el-select v-model="form.locationId" placeholder="请选择货架" :disabled="!form.areaId" size="small">
<el-option
v-for="location in locationList"
:key="location.locationId"
......@@ -177,4 +177,7 @@ if (props.modelValue.warehouseId) {
background-color: #f5f7fa;
border-radius: 4px;
}
:deep(.el-form-item) {
margin-bottom: 10px;
}
</style>
\ No newline at end of file
......@@ -17,30 +17,6 @@
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="所属库区" prop="areaId">
<el-input
v-model="queryParams.areaId"
placeholder="请输入所属库区ID"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="X坐标" prop="xCoordinate">
<el-input
v-model="queryParams.xCoordinate"
placeholder="请输入X坐标"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="Y坐标" prop="yCoordinate">
<el-input
v-model="queryParams.yCoordinate"
placeholder="请输入Y坐标"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
......@@ -51,7 +27,7 @@
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="货架编码" align="center" prop="locationCode" />
<el-table-column label="货架名称" align="center" prop="locationName" />
<el-table-column label="所属库区" align="center" prop="areaId" />
<el-table-column label="所属库区" align="center" prop="area.areaName" />
<el-table-column label="货架状态" align="center" prop="status" >
<template #default="scope">
<dict-tag :options="sys_normal_disable" :value="scope.row.status" />
......
......@@ -34,12 +34,9 @@
/>
</el-form-item>
<el-form-item label="标签状态" prop="tagStatus">
<el-input
v-model="queryParams.tagStatus"
placeholder="请输入标签状态"
clearable
@keyup.enter="handleQuery"
/>
<el-select v-model="queryParams.tagStatus" placeholder="请选择标签状态">
<el-option v-for="item in is_used" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
......@@ -49,19 +46,13 @@
<el-table v-loading="loading" :data="rfidTagList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="标签ID" align="center" prop="tagId" />
<el-table-column label="标签编码" align="center" prop="tagCode" />
<el-table-column label="EPC编码" align="center" prop="epcCode" />
<el-table-column label="TID编码" align="center" prop="tidCode" />
<el-table-column label="标签类型" align="center" prop="tagType" />
<el-table-column label="标签状态" align="center" prop="tagStatus">
<template #default="scope">
<dict-tag :options="sys_normal_disable" :value="scope.row.tagStatus" />
</template>
</el-table-column>
<el-table-column label="最后读取时间" align="center" prop="lastReadTime">
<template #default="scope">
<span>{{ parseTime(scope.row.lastReadTime, '{y}-{m}-{d}') }}</span>
<dict-tag :options="is_used" :value="scope.row.tagStatus" />
</template>
</el-table-column>
</el-table>
......@@ -88,7 +79,7 @@ import { ref, reactive, watch } from 'vue'
import { listWmsRfidTag } from "@/api/ware/wmsRfidTag"
import { parseTime } from "@/utils/ruoyi"
const {proxy} = getCurrentInstance()
const { sys_normal_disable } = proxy.useDict('sys_normal_disable')
const { is_used } = proxy.useDict('is_used')
const props = defineProps({
visible: {
type: Boolean,
......@@ -129,7 +120,7 @@ const queryParams = reactive({
epcCode: undefined,
tidCode: undefined,
tagType: undefined,
tagStatus: undefined
tagStatus: '0'
})
// 表格数据
......@@ -148,7 +139,7 @@ const init = () => {
queryParams.epcCode = undefined
queryParams.tidCode = undefined
queryParams.tagType = undefined
queryParams.tagStatus = undefined
queryParams.tagStatus = '0'
}
// 查询数据
......
......@@ -6,7 +6,7 @@
append-to-body
>
<el-table :data="adjustInventoryDetailList" border style="width: 100%" height="600px">
<el-table-column prop="wmsMaterial.materialName" label="物资名称" width="200" fixed="left" />
<el-table-column prop="wmsMaterial.materialName" label="物资名称" width="120" fixed="left" />
<el-table-column prop="batchNo" label="批次号" width="150" />
<el-table-column prop="actualQuantity" label="实际数量" width="120">
<template #default="scope">
......@@ -42,12 +42,12 @@
<span v-else>{{ scope.row.unitPrice }}</span>
</template>
</el-table-column>
<el-table-column label="金额" width="100">
<el-table-column label="金额" width="80">
<template #default="scope">
<span>{{ scope.row.amount }}</span>
</template>
</el-table-column>
<el-table-column prop="locationId" label="货架" width="300">
<el-table-column prop="locationId" label="货架" width="180">
<template #default="scope">
<LocationCascaderSelect
v-if="scope.row.isEditing"
......@@ -62,21 +62,27 @@
</template>
</el-table-column>
<!-- PLT标签 -->
<el-table-column prop="storageLocation" label="PTL标签" width="180">
<el-table-column prop="storageLocation" label="PTL标签" width="150">
<template #default="scope">
<div v-if="scope.row.isEditing">
<el-input v-model="scope.row.storageLocation" placeholder="请选择PTL标签" size="small" readonly />
<el-button slot="append" type="primary" size="small" @click="openPtlTagSelect(scope.$index)">选择</el-button>
<el-input v-model="scope.row.storageLocation" placeholder="请选择PTL标签" size="small" readonly>
<template #append>
<el-button slot="append" type="primary" size="small" @click="openPtlTagSelect(scope.$index)">选择</el-button>
</template>
</el-input>
</div>
<span v-else>{{ scope.row.storageLocation || '-' }}</span>
</template>
</el-table-column>
<!-- RFID标签 -->
<el-table-column prop="rfidTagIds" label="RFID标签" width="180">
<el-table-column prop="rfidTagIds" label="RFID标签" width="150">
<template #default="scope">
<div v-if="scope.row.isEditing">
<el-input v-model="scope.row.rfidTagCodes" placeholder="请选择RFID标签" size="small" readonly />
<el-button slot="append" type="primary" size="small" @click="openRfidTagSelect(scope.$index)">选择</el-button>
<el-input v-model="scope.row.rfidTagCodes" placeholder="请选择RFID标签" size="small" readonly>
<template #append>
<el-button slot="append" type="primary" size="small" @click="openRfidTagSelect(scope.$index)">选择</el-button>
</template>
</el-input>
</div>
<span v-else>{{ scope.row.rfidTagCodes || '-' }}</span>
</template>
......
<template>
<el-dialog title="入库操作" v-model="dialogVisible" width="1200px" append-to-body>
<el-form :model="inventoryQueryParams" ref="inventoryQueryRef" :inline="true" label-width="120px">
<el-form-item label="批次号" prop="batchNo">
<el-input v-model="inventoryQueryParams.batchNo" placeholder="请输入或扫描批次号" clearable
@keyup.enter="handleInventoryQuery" @input="handleBatchNoInput" />
<el-form-item label="物资编码/名称" prop="materialCodeOrName">
<el-input v-model="inventoryQueryParams.materialCodeOrName" placeholder="请输入物资编码/名称" clearable
@keyup.enter="handleInventoryQuery" @input="handleMaterialCodeOrNameInput" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleInventoryQuery">搜索</el-button>
......@@ -11,64 +11,41 @@
</el-form-item>
</el-form>
<el-table :data="inventoryDetailList" border style="width: 100%; margin-top: 20px;" height="600px">
<el-table-column prop="wmsMaterial.materialName" label="物资名称" width="200" />
<el-table-column prop="batchNo" label="批次号" width="150" />
<el-table-column prop="planQuantity" label="计划数量" width="100" />
<el-table-column prop="actualQuantity" label="实际数量" width="100">
<template #default="scope">
<el-input v-model.number="scope.row.actualQuantity" placeholder="请输入实际数量" size="small"
@change="calculateAmount(scope.row)" type="number" :precision="3" :step="0.001" />
<el-table :data="inventoryDetailList" border height="600px">
<el-table-column type="expand">
<template #default="props">
<el-table :data="props.row.inBoundItem">
<el-table-column prop="batchNo" label="批次号" width="150" />
<el-table-column prop="actualQuantity" label="入库数量" width="100" />
<el-table-column prop="unitPrice" label="单价" width="100" />
<el-table-column label="金额" width="100" prop="amount" />
<el-table-column prop="locationId" label="货架" width="300" />
<el-table-column prop="storageLocation" label="PTL标签"/>
<el-table-column prop="rfidTagIds" label="RFID标签" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right">
<template #default="scope">
<el-button link v-if="scope.row.isCanceled === '0'" type="primary"
@click="handleCancelByRelation(scope.row)">撤销</el-button>
</template>
</el-table-column>
</el-table>
</template>
</el-table-column>
<el-table-column prop="wmsMaterial.materialName" label="物资名称" width="200" />
<el-table-column prop="wmsMaterial.materialCode" label="物资编码" width="150" />
<el-table-column prop="wmsMaterial.specification" label="规格型号" width="150" />
<el-table-column prop="planQuantity" label="计划数量" width="100" />
<el-table-column prop="unitPrice" label="单价" width="100" />
<el-table-column label="金额" width="100">
<template #default="scope">
<span>{{ scope.row.amount }}</span>
</template>
</el-table-column>
<el-table-column prop="locationId" label="货架" width="300">
<template #default="scope">
<LocationCascaderSelect
:model-value="{ warehouseId: inboundOrder?.warehouseId, areaId: scope.row.areaId, locationId: scope.row.locationId }"
@update:model-value="(value) => {
if (value.locationId) {
// 检查是否有其他行使用了相同的locationId
const duplicateRow = inventoryDetailList.find((item, index) =>
index !== scope.$index && item.locationId === value.locationId
);
if (duplicateRow) {
proxy.$message.warning('该货架已被其他明细行使用,请选择其他货架');
return;
}
}
scope.row.warehouseId = inboundOrder?.warehouseId;
scope.row.areaId = value.areaId;
scope.row.locationId = value.locationId;
}" />
</template>
</el-table-column>
<el-table-column prop="storageLocation" label="PTL标签" width="180">
<template #default="scope">
<el-input v-model="scope.row.storageLocation" placeholder="请选择PTL标签" size="small" readonly />
<el-button slot="append" type="primary" size="small" @click="openPtlTagSelect(scope.$index)">选择</el-button>
</template>
</el-table-column>
<el-table-column prop="rfidTagIds" label="RFID标签" width="180">
<template #default="scope">
<el-input v-model="scope.row.rfidTagCodes" placeholder="请选择RFID标签" size="small" readonly />
<el-button slot="append" type="primary" size="small" @click="openRfidTagSelect(scope.$index)">选择</el-button>
</template>
</el-table-column>
<el-table-column prop="unit" label="计量单位" />
<el-table-column label="金额" width="100" prop="amount" />
<el-table-column prop="remark" label="备注">
<template #default="scope">
{{ scope.row.remark }}
</template>
</el-table-column>
<el-table-column prop="wmsInboundOrderItemId" label="操作" width="100" fixed="right">
<el-table-column label="操作" width="100" fixed="right">
<template #default="scope">
<el-button type="primary" size="small" @click="handleExportQrcode(scope.row)">打印二维码</el-button>
<el-button type="primary" size="small" @click="handleAddInBoundItem(scope.row)">添加入库</el-button>
</template>
</el-table-column>
</el-table>
......@@ -76,7 +53,6 @@
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogVisible = false">取 消</el-button>
<el-button type="primary" @click="confirmInventory">确认入库</el-button>
</div>
</template>
......@@ -91,17 +67,21 @@
<!-- 二维码打印对话框组件 -->
<QrcodePrintDialog ref="qrcodePrintDialogRef" v-model:visible="qrcodePrintDialogVisible"
:qrcode-value="currentQrcodeValue" @print="handleQrcodePrint" />
<!-- 添加入库明细对话框组件 -->
<AddInBoundItemDialog ref="addInBoundItemDialogRef" @submit="handleAddInBoundItemSubmit" />
</el-dialog>
</template>
<script setup>
import { ref, computed, getCurrentInstance } from 'vue'
import { listWmsInboundOrderItem } from "@/api/ware/wmsInboundOrderItem"
import { listWmsInboundOrderItemDetail } from "@/api/ware/wmsInboundOrderItemDetail"
import { addToInventory } from "@/api/ware/wmsInboundOrder"
import RfidTagSelectDialog from "@/components/RfidTagSelectDialog.vue"
import PtlTagSelectDialog from "@/components/PtlTagSelectDialog.vue"
import QrcodePrintDialog from "@/components/QrcodePrintDialog.vue"
import LocationCascaderSelect from "@/components/LocationCascaderSelect.vue"
import AddInBoundItemDialog from "@/views/ware/wmsInboundOrder/components/AddInBoundItemDialog.vue"
const props = defineProps({
visible: {
......@@ -150,6 +130,8 @@ const qrcodePrintDialogRef = ref(null)
const currentEditingRowIndex = ref(-1)
// 当前要打印的二维码值
const currentQrcodeValue = ref('')
// 添加入库明细对话框状态
const addInBoundItemDialogRef = ref(null)
// 处理批次号输入(支持扫描枪)
function handleBatchNoInput() {
......@@ -172,7 +154,7 @@ function calculateAmount(row) {
// 入库明细查询
function handleInventoryQuery() {
listWmsInboundOrderItem({ batchNo: inventoryQueryParams.value.batchNo, orderId: inboundOrder.value.orderId, putawayStatus: '0' }).then(response => {
listWmsInboundOrderItemDetail({ orderId: inboundOrder.value.orderId, putawayStatus: '0' }).then(response => {
if (response.rows.length) {
inventoryDetailList.value = response.rows;
} else {
......@@ -294,9 +276,17 @@ function handleQrcodePrint() {
function openDialog(currentInboundOrder) {
inboundOrder.value = currentInboundOrder
inventoryDetailList.value = []
handleInventoryQuery()
}
function handleAddInBoundItem(row) {
addInBoundItemDialogRef.value?.handleAdd(row)
}
function handleAddInBoundItemSubmit(inBoundOrderItem) {
}
defineExpose({
openDialog
})
......
......@@ -34,10 +34,13 @@
<el-table v-loading="loading" :data="wmsInventoryList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" fixed="left" />
<el-table-column label="物资ID" align="center" prop="materialId" fixed="left" />
<!-- <el-table-column label="仓库ID" align="center" prop="warehouseId" /> -->
<!-- <el-table-column label="库区ID" align="center" prop="areaId" /> -->
<el-table-column label="货架ID" align="center" prop="locationId" fixed="left" />
<el-table-column label="物资名称" align="center" prop="material.materialName" fixed="left" />
<el-table-column label="物资编号" align="center" prop="material.materialCode" fixed="left" />
<el-table-column label="规格型号" align="center" prop="material.specification" fixed="left" />
<el-table-column label="计量单位" align="center" prop="material.unit" fixed="left" />
<el-table-column label="仓库" align="center" prop="warehouse.warehouseName" />
<el-table-column label="库区" align="center" prop="area.areaName" />
<el-table-column label="货架" align="center" prop="location.locationName"/>
<el-table-column label="批次号" align="center" prop="batchNo" width="180" />
<el-table-column label="生产日期" align="center" prop="productionDate" width="180">
<template #default="scope">
......@@ -52,7 +55,7 @@
<el-table-column label="库存数量" align="center" prop="quantity" />
<el-table-column label="可用数量" align="center" prop="availableQuantity" />
<el-table-column label="锁定数量" align="center" prop="lockedQuantity" />
<el-table-column label="单位成本" align="center" prop="unitCost" />
<el-table-column label="入库单价" align="center" prop="unitCost" />
<el-table-column label="总成本" align="center" prop="totalCost" />
<!-- <el-table-column label="质量状态" align="center" prop="qualityStatus" /> -->
<el-table-column label="库存状态" align="center" prop="inventoryStatus">
......@@ -75,7 +78,6 @@
<span>{{ scope.row.rfidTag || '-' }}</span>
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="RFID绑定数量" align="center" prop="rfidBindingCount" />
<el-table-column label="RFID覆盖率" align="center" prop="rfidCoverageRate" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="220" fixed="right">
......@@ -190,7 +192,7 @@
</el-col>
</el-row>
<!-- 第七行:锁定数量、单位成本 -->
<!-- 第七行:锁定数量、入库单价 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="锁定数量" prop="lockedQuantity">
......@@ -198,8 +200,8 @@
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="单位成本" prop="unitCost">
<el-input v-model="form.unitCost" placeholder="请输入单位成本" />
<el-form-item label="入库单价" prop="unitCost">
<el-input v-model="form.unitCost" placeholder="请输入入库单价" />
</el-form-item>
</el-col>
</el-row>
......@@ -317,17 +319,50 @@
<div class="location-visual-container">
<!-- 第一列:货架信息 -->
<div class="visual-column location-info">
<h3>当前货架信息</h3>
<el-form :model="selectedLocationInfo" label-width="80px" class="three-col-form">
<h3>库存基础信息</h3>
<el-form :model="selectedInventoryInfo" label-width="80px" class="three-col-form">
<div class="form-row">
<el-form-item label="货架编码">
<span>{{ selectedLocationInfo.locationCode || '-' }}</span>
<el-form-item label="物资名称">
<span>{{ selectedInventoryInfo.materialName || '-' }}</span>
</el-form-item>
<el-form-item label="货架名称">
<span>{{ selectedLocationInfo.locationName || '-' }}</span>
<el-form-item label="物资编号">
<span>{{ selectedInventoryInfo.materialCode || '-' }}</span>
</el-form-item>
<el-form-item label="库区名称">
<span>{{ selectedLocationInfo.areaId || '-' }}</span>
<el-form-item label="规格型号">
<span>{{ selectedInventoryInfo.specification || '-' }}</span>
</el-form-item>
</div>
<div class="form-row">
<el-form-item label="计量单位">
<span>{{ selectedInventoryInfo.unit || '-' }}</span>
</el-form-item>
<el-form-item label="批次号">
<span>{{ selectedInventoryInfo.batchNo || '-' }}</span>
</el-form-item>
<el-form-item label="生产日期">
<span>{{ parseTime(selectedInventoryInfo.productionDate, '{y}-{m}-{d}') || '-' }}</span>
</el-form-item>
</div>
<div class="form-row">
<el-form-item label="有效日期">
<span>{{ parseTime(selectedInventoryInfo.expirationDate, '{y}-{m}-{d}') || '-' }}</span>
</el-form-item>
<el-form-item label="库存数量">
<span>{{ selectedInventoryInfo.quantity || 0 }}</span>
</el-form-item>
<el-form-item label="可用数量">
<span>{{ selectedInventoryInfo.availableQuantity || 0 }}</span>
</el-form-item>
</div>
<div class="form-row">
<el-form-item label="锁定数量">
<span>{{ selectedInventoryInfo.lockedQuantity || 0 }}</span>
</el-form-item>
<el-form-item label="入库单价">
<span>{{ selectedInventoryInfo.unitCost || 0 }}</span>
</el-form-item>
<el-form-item label="总成本">
<span>{{ selectedInventoryInfo.totalCost || 0 }}</span>
</el-form-item>
</div>
</el-form>
......@@ -402,6 +437,22 @@ const selectedLocationInfo = reactive({
materialCount: '0'
})
// 当前选中的库存信息
const selectedInventoryInfo = reactive({
materialName: '',
materialCode: '',
specification: '',
unit: '',
batchNo: '',
productionDate: '',
expirationDate: '',
quantity: 0,
availableQuantity: 0,
lockedQuantity: 0,
unitCost: 0,
totalCost: 0
})
// 存储所有货架的列表
const locations = ref([])
// 存储所有货架的单元格数据,结构:{ locationId: string, cells: Cell[] }
......@@ -702,6 +753,21 @@ function handleLocationVisualization(row) {
listWmsLocation({ pageNum: 1, pageSize: 1000 }).then(response => {
locations.value = response.rows;
})
// 保存当前库存信息
Object.assign(selectedInventoryInfo, {
materialName: row.material?.materialName || '',
materialCode: row.material?.materialCode || '',
specification: row.material?.specification || '',
unit: row.material?.unit || '',
batchNo: row.batchNo || '',
productionDate: row.productionDate || '',
expirationDate: row.expirationDate || '',
quantity: row.quantity || 0,
availableQuantity: row.availableQuantity || 0,
lockedQuantity: row.lockedQuantity || 0,
unitCost: row.unitCost || 0,
totalCost: row.totalCost || 0
})
locationVisualDialogVisible.value = true
}
......
<template>
<div class="outbound-manager-container">
<!-- 顶部导航和操作栏 -->
<div class="header-section">
<el-breadcrumb separator="/">
<el-breadcrumb-item>仓库管理</el-breadcrumb-item>
<el-breadcrumb-item>出库作业</el-breadcrumb-item>
<el-breadcrumb-item>批量出库</el-breadcrumb-item>
</el-breadcrumb>
<div class="header-actions">
<el-button type="primary" @click="createNewOrder">
<el-icon><Plus /></el-icon>新建出库单
</el-button>
<el-button @click="refreshData">
<el-icon><Refresh /></el-icon>刷新
</el-button>
</div>
</div>
<!-- 标签页:出库单列表 和 出库执行 -->
<el-tabs v-model="activeTab" type="border-card" class="main-tabs">
<!-- 标签页1: 出库单列表 -->
<el-tab-pane label="出库单管理" name="orderList">
<OrderList
:orders="orders"
@select-order="handleSelectOrder"
@edit-order="handleEditOrder"
@delete-order="handleDeleteOrder"
@start-outbound="handleStartOutbound"
/>
</el-tab-pane>
<!-- 标签页2: 出库执行 (仅在选中订单后可用) -->
<el-tab-pane
label="出库执行"
name="outboundExecution"
:disabled="!selectedOrder"
>
<OutboundExecution
v-if="selectedOrder"
:order="selectedOrder"
:key="selectedOrder.id"
@order-updated="handleOrderUpdated"
@operation-log="handleOperationLog"
/>
</el-tab-pane>
<!-- 标签页3: 操作日志 -->
<el-tab-pane label="操作日志" name="operationLogs">
<OperationLogs :logs="operationLogs" />
</el-tab-pane>
</el-tabs>
<!-- 新建/编辑出库单对话框 -->
<OrderDialog
v-model="showOrderDialog"
:order="editingOrder"
mode="create"
@save="handleSaveOrder"
/>
</div>
</template>
<script setup>
import { ref, onMounted, computed } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Plus, Refresh } from '@element-plus/icons-vue'
import OrderList from './components/OrderList.vue'
import OutboundExecution from './components/OutboundExecution.vue'
import OperationLogs from './components/OperationLogs.vue'
import OrderDialog from './components/OrderDialog.vue'
import {
fetchOrders,
deleteOrder,
createOrder,
fetchOperationLogs
} from './components/mockApi'
// 响应式数据
const activeTab = ref('orderList')
const orders = ref([])
const selectedOrder = ref(null)
const showOrderDialog = ref(false)
const editingOrder = ref(null)
const operationLogs = ref([])
// 生命周期
onMounted(() => {
loadOrders()
loadOperationLogs()
})
// 方法
const loadOrders = async () => {
try {
orders.value = await fetchOrders()
} catch (error) {
ElMessage.error('加载出库单失败')
}
}
const loadOperationLogs = async () => {
try {
operationLogs.value = await fetchOperationLogs()
} catch (error) {
console.error('加载操作日志失败:', error)
}
}
const refreshData = () => {
loadOrders()
loadOperationLogs()
ElMessage.success('数据已刷新')
}
const createNewOrder = () => {
editingOrder.value = null
showOrderDialog.value = true
}
const handleSelectOrder = (order) => {
selectedOrder.value = order
activeTab.value = 'outboundExecution'
}
const handleEditOrder = (order) => {
editingOrder.value = { ...order }
showOrderDialog.value = true
}
const handleDeleteOrder = async (order) => {
try {
await ElMessageBox.confirm(
`确定要删除出库单 ${order.orderNo} 吗?`,
'确认删除',
{ type: 'warning' }
)
await deleteOrder(order.id)
ElMessage.success('删除成功')
loadOrders()
} catch (error) {
// 用户取消
}
}
const handleStartOutbound = (order) => {
selectedOrder.value = order
activeTab.value = 'outboundExecution'
}
const handleSaveOrder = async (orderData) => {
try {
await createOrder(orderData)
ElMessage.success('出库单创建成功')
showOrderDialog.value = false
loadOrders()
} catch (error) {
ElMessage.error('保存失败')
}
}
const handleOrderUpdated = () => {
loadOrders()
}
const handleOperationLog = (log) => {
operationLogs.value.unshift(log)
}
</script>
<style scoped>
.outbound-manager-container {
height: 100vh;
display: flex;
flex-direction: column;
background: #f5f7fa;
padding: 20px;
}
.header-section {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding: 0 10px;
}
.header-actions {
display: flex;
gap: 10px;
}
.main-tabs {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
}
:deep(.el-tabs__content) {
flex: 1;
overflow: auto;
}
</style>
\ No newline at end of file
<template>
<el-dialog
v-model="visible"
:title="dialogTitle"
width="1200px"
:close-on-click-modal="false"
>
<el-dialog v-model="visible" :title="dialogTitle" width="1200px" :close-on-click-modal="false">
<!-- 物资信息摘要 -->
<div v-if="detail" class="material-summary">
<el-descriptions :column="3" border size="small">
<el-descriptions-item label="物资编码">
{{ detail.materialCode }}
{{ detail.material?.materialCode }}
</el-descriptions-item>
<el-descriptions-item label="物资名称">
{{ detail.materialName }}
{{ detail.material?.materialName }}
</el-descriptions-item>
<el-descriptions-item label="规格型号">
{{ detail.specification }}
{{ detail.material?.specification }}
</el-descriptions-item>
<el-descriptions-item label="计划数量">
<span class="planned-quantity">{{ detail.plannedQuantity }}</span>
<el-descriptions-item label="计划出库数量">
<span class="planned-quantity">{{ detail.planQuantity }}</span>
</el-descriptions-item>
<el-descriptions-item label="实际出库">
<span :class="{
'completed': detail.actualQuantity >= detail.plannedQuantity,
'in-progress': detail.actualQuantity > 0 && detail.actualQuantity < detail.plannedQuantity
}">
{{ detail.actualQuantity }}
</span>
<el-descriptions-item label="实际出库数量">
<span class="actual-quantity">{{ detail.actualQuantity }}</span>
</el-descriptions-item>
<el-descriptions-item label="剩余需求">
<span class="remaining-need">
{{ Math.max(0, detail.plannedQuantity - detail.actualQuantity) }}
<el-descriptions-item label="拣货状态">
<span class="picked-status">
<dict-tag :options="out_order_status" :value="detail.pickingStatus" />
</span>
</el-descriptions-item>
</el-descriptions>
<el-progress
:percentage="completionPercentage"
:status="completionPercentage >= 100 ? 'success' : 'primary'"
:stroke-width="12"
style="margin-top: 15px;"
/>
</div>
<!-- 批次出库记录 -->
......@@ -51,48 +34,36 @@
{{ transactions.length }} 个批次
</span>
</div>
<el-table
:data="transactions"
stripe
border
size="small"
height="300px"
>
<el-table-column type="selection" width="55" align="center" fixed="left" />
<el-table-column type="index" label="序号" width="60" align="center" fixed="left" />
<el-table-column label="关联ID" align="center" prop="relationId" />
<el-table-column label="出库明细ID" align="center" prop="itemId" />
<el-table-column label="出库单ID" align="center" prop="orderId" />
<el-table-column label="库存ID" align="center" prop="inventoryId" />
<el-table-column label="RFID标签ID" align="center" prop="rfidTagId" />
<el-table-column label="RFID编码" align="center" prop="rfidCode" />
<el-table-column label="EPC编码" align="center" prop="epcCode" />
<el-table-column label="TID编码" align="center" prop="tidCode" />
<el-table-column label="物资ID" align="center" prop="materialId" />
<el-table-column label="批次号" align="center" prop="batchNo" width="120" />
<el-table-column label="仓库id" align="center" prop="warehouseId" />
<el-table-column label="库区id" align="center" prop="areaId" />
<el-table-column label="库位ID" align="center" prop="locationId" />
<el-table-column label="数量" align="center" prop="quantity" />
<el-table-column label="单位成本" align="center" prop="unitCost" />
<el-table-column label="出库时间" align="center" prop="outboundTime" width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.outboundTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="是否已撤销" align="center" prop="isCanceled" />
<el-table-column label="撤销时间" align="center" prop="cancelTime" width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.cancelTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="撤销人" align="center" prop="cancelBy" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right">
<template #default="scope">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['ware:wmsOutboundItemInventory:remove']">撤销</el-button>
</template>
</el-table-column>
<el-table :data="transactions" stripe border size="small" height="300px">
<el-table-column type="index" label="序号" width="60" align="center" fixed="left" />
<el-table-column label="RFID标签" align="center" prop="rfidCode" />
<el-table-column label="批次号" align="center" prop="batchNo" width="120" />
<el-table-column label="仓库" align="center" prop="warehouse.warehouseName" />
<el-table-column label="库区" align="center" prop="area.areaName" />
<el-table-column label="货架" align="center" prop="location.locationName" />
<el-table-column label="数量" align="center" prop="quantity" />
<el-table-column label="入库单价" align="center" prop="unitCost" />
<el-table-column label="出库时间" align="center" prop="outboundTime" width="110">
<template #default="scope">
<span>{{ parseTime(scope.row.outboundTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="是否撤销" align="center" prop="isCanceled">
<template #default="scope">
<dict-tag :options="is_cancel" :value="scope.row.isCanceled" />
</template>
</el-table-column>
<el-table-column label="撤销时间" align="center" prop="cancelTime" width="110">
<template #default="scope">
<span>{{ parseTime(scope.row.cancelTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right">
<template #default="scope">
<el-button link v-if="scope.row.isCanceled === '0'" type="primary" @click="handleCancelByRelation(scope.row)">撤销</el-button>
</template>
</el-table-column>
</el-table>
</div>
......@@ -123,11 +94,8 @@
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false">关闭</el-button>
<el-button
v-if="detail?.actualQuantity < detail?.plannedQuantity"
type="primary"
@click="handleContinueOutbound"
>
<el-button v-if="detail?.actualQuantity < detail?.plannedQuantity" type="primary"
@click="handleContinueOutbound">
继续出库
</el-button>
</span>
......@@ -139,6 +107,12 @@
import { ref, computed, watch } from 'vue'
import { Close } from '@element-plus/icons-vue'
import { listWmsOutboundItemInventory } from "@/api/ware/wmsOutboundItemInventory"
import useUserStore from '@/store/modules/user'
import { cancelByRelationId } from '@/api/ware/wmsOutboundOrder'
const { proxy } = getCurrentInstance()
const { outbound_type, out_order_status, is_cancel } = proxy.useDict('outbound_type', 'out_order_status', 'is_cancel')
const props = defineProps({
modelValue: {
type: Boolean,
......@@ -146,6 +120,7 @@ const props = defineProps({
}
})
const userStore = useUserStore()
const detail = ref({})
const transactions = ref([])
......@@ -160,7 +135,7 @@ const visible = computed({
// 计算属性
const dialogTitle = computed(() => {
if (!detail.value) return '批次详情'
return `${detail.value.materialName} - 批次出库详情`
return `${detail.value.material?.materialName || ''} - 批次出库详情`
})
const completionPercentage = computed(() => {
......@@ -178,7 +153,7 @@ const normalTransactionCount = computed(() => {
})
const reversedTransactionCount = computed(() => {
return transactions.value.filter(t => t.status === 'REVERSED').length
return transactions.value.filter(t => t.isCanceled === '1').length
})
const totalAmount = computed(() => {
......@@ -209,16 +184,37 @@ const handleContinueOutbound = () => {
visible.value = false
}
const openDialog = (row) => {
detail.value = row
const openDialog = (outboundOrderItem) => {
detail.value = outboundOrderItem
listWmsOutboundItemInventory({
orderId: row.orderId,
itemId: outboundOrderItem.itemId,
pageNum: 1,
pageSize: 1000
}).then(res => {
transactions.value = res.rows || []
})
}
/**
* 撤销关联流水
* @param row 关联流水行数据
*/
const handleCancelByRelation = async (row) => {
try {
const result = await cancelByRelationId(row, userStore.id)
if (result.code !== 200) {
ElMessage.error(result.msg || '撤销失败')
return
}
row.isCanceled = '1'
ElMessage.success('已撤销成功')
} catch (error) {
console.error('撤销失败:', error)
ElMessage.error('撤销失败')
}
}
defineExpose({
openDialog
})
......@@ -326,7 +322,7 @@ defineExpose({
}
:deep(.el-descriptions__label) {
width: 80px;
width: 120px;
font-weight: 500;
}
......
<template>
<div class="app-container">
<el-tabs v-model="activeTab" tab-position="top" @tab-click="handleTabClick">
<el-tab-pane v-for="item in panes" :key="item.key" :label="item.label" :name="item.key">
<component :is="item.component" />
</el-tab-pane>
</el-tabs>
</div>
</template>
<script setup name="WmsSpecial">
import { ref } from 'vue'
const activeTab = ref('first')
const panes = ref([
{
key: 'first',
label: '入库',
component: () => import('@/views/ware/wmsSpecial/InBoundOrder.vue')
},
{
key: 'second',
label: '出库',
component: () => import('@/views/ware/wmsSpecial/OutBoundOrder.vue')
}
])
</script>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment