Commit 6a357ea1 by 杨子

feat(出库执行): 新增智能批次推荐功能并优化界面

refactor(库存查询): 调整库存汇总表格显示和排序逻辑

style(表单): 统一调整表单标签宽度为80px

fix(预警规则): 修复阈值输入和启用状态显示问题

perf(库存可视化): 优化库存信息显示布局和字段标签

chore(物资选择): 移除安全库存字段并调整状态切换方式
parent 94f94f90
......@@ -82,4 +82,15 @@ export function cancelByRelationId(data, operatorId) {
operatorId: operatorId
}
})
}
\ No newline at end of file
}
/**
* 按策略查询库存数据
*/
export function getInventoryByStrategy(data) {
return request({
url: '/ware/wmsOutboundOrder/getInventoryByStrategy',
method: 'post',
data: data
})
}
......@@ -4,7 +4,7 @@
<el-row :gutter="10">
<el-col :span="8">
<el-form-item label="仓库">
<el-select v-model="form.warehouseId" placeholder="请选择仓库" @change="handleWarehouseChange" size="small">
<el-select v-model="form.warehouseId" placeholder="请选择仓库" @change="handleWarehouseChange" size="small" filterable clearable>
<el-option
v-for="warehouse in warehouseList"
:key="warehouse.warehouseId"
......@@ -16,7 +16,7 @@
</el-col>
<el-col :span="8">
<el-form-item label="库区">
<el-select v-model="form.areaId" placeholder="请选择库区" @change="handleAreaChange" :disabled="!form.warehouseId" size="small">
<el-select v-model="form.areaId" placeholder="请选择库区" @change="handleAreaChange" :disabled="!form.warehouseId" size="small" filterable clearable>
<el-option
v-for="area in areaList"
:key="area.areaId"
......@@ -28,7 +28,7 @@
</el-col>
<el-col :span="8">
<el-form-item label="货架">
<el-select v-model="form.locationId" placeholder="请选择货架" :disabled="!form.areaId" size="small">
<el-select v-model="form.locationId" placeholder="请选择货架" :disabled="!form.areaId" size="small" filterable clearable>
<el-option
v-for="location in locationList"
:key="location.locationId"
......
......@@ -59,7 +59,6 @@
<el-table-column label="规格型号" align="center" prop="specification" />
<el-table-column label="计量单位" align="center" prop="unit" />
<el-table-column label="生产厂家" align="center" prop="manufacturer" />
<el-table-column label="安全库存" align="center" prop="safetyStock" />
<el-table-column label="状态" align="center" prop="status" >
<template #default="scope">
<dict-tag :options="sys_normal_disable" :value="scope.row.status" />
......@@ -131,13 +130,8 @@
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="安全库存" prop="safetyStock">
<el-input v-model.number="addMaterialForm.safetyStock" placeholder="请输入安全库存" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="状态" prop="status">
<el-switch v-model="addMaterialForm.status" />
<el-switch v-model="addMaterialForm.status" active-value="0" inactive-value="1" active-text="正常" inactive-text="停用" inline-prompt />
</el-form-item>
</el-col>
</el-row>
......@@ -209,7 +203,7 @@ const addMaterialForm = reactive({
unit: '',
manufacturer: '',
safetyStock: null,
status: true
status: '0'
})
// 新增物资表单验证规则
......@@ -293,8 +287,6 @@ const handleAddMaterial = () => {
if (addMaterialRef.value) {
addMaterialRef.value.resetFields()
}
// 设置默认状态为启用
addMaterialForm.status = true
addMaterialVisible.value = true
}
......
......@@ -17,15 +17,7 @@
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="阈值" prop="thresholdValue">
<el-input
v-model="queryParams.thresholdValue"
placeholder="请输入阈值"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="预警级别" prop="alertLevel">
<el-select
v-model="queryParams.alertLevel"
......@@ -149,7 +141,7 @@
</el-form-item>
<el-form-item label="阈值" prop="thresholdValue">
<el-input v-model="form.thresholdValue" placeholder="请输入阈值" />
<el-input-number v-model="form.thresholdValue" placeholder="请输入阈值" :precision="2" />
</el-form-item>
<el-form-item label="比较运算符" prop="comparisonOperator">
<el-select v-model="form.comparisonOperator" placeholder="请选择比较运算符">
......@@ -163,9 +155,7 @@
</el-form-item>
<el-form-item label="是否启用" prop="isEnabled">
<el-select v-model="form.isEnabled" placeholder="请选择是否启用">
<el-option v-for="item in is_enabled" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<el-switch v-model="form.isEnabled" active-value="1" inactive-value="2" active-text="启用" inactive-text="禁用" inline-prompt />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
......@@ -238,7 +228,7 @@ const data = reactive({
alertLevel: null,
notificationType: null,
notificationReceivers: null,
isEnabled: null,
isEnabled: '1',
},
rules: {
ruleName: [
......@@ -301,7 +291,7 @@ function reset() {
alertLevel: null,
notificationType: null,
notificationReceivers: null,
isEnabled: null,
isEnabled: '1',
remark: null,
createBy: null,
createTime: null,
......@@ -343,6 +333,7 @@ function handleUpdate(row) {
const _ruleId = row.ruleId || ids.value
getWmsAlertRule(_ruleId).then(response => {
form.value = response.data
form.value.materialName = row.material?.materialName || ''
open.value = true
title.value = "修改预警规则"
})
......
......@@ -11,7 +11,7 @@
</el-form-item>
</el-form>
<el-table ref="inventoryTableRef" :data="inventoryDetailList" row-key="itemDetailId" border height="600px"
<el-table ref="inventoryTableRef" :data="inventoryDetailList" row-key="itemDetailId" border height="calc(100vh - 200px)"
@expand-change="handleExpandChange">
<el-table-column type="expand" fixed="left">
<template #default="props">
......
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="120px">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="80px">
<el-form-item label="入库单号" prop="orderNo">
<el-input v-model="queryParams.orderNo" placeholder="请输入入库单号" clearable @keyup.enter="handleQuery" />
</el-form-item>
......
......@@ -4,7 +4,7 @@
<!-- 第一列:库存基础信息 -->
<div class="visual-column location-info">
<h3>库存基础信息</h3>
<el-form :model="inventoryInfo" label-width="80px" class="three-col-form">
<el-form :model="inventoryInfo" label-width="80px" label-position="top" class="three-col-form">
<div class="form-row">
<el-form-item label="物资名称">
<span>{{ inventoryInfo.materialName || '-' }}</span>
......@@ -42,10 +42,10 @@
<el-form-item label="锁定数量">
<span>{{ inventoryInfo.lockedQuantity || 0 }}</span>
</el-form-item>
<el-form-item label="入库单价">
<el-form-item label="入库单价(元)">
<span>{{ inventoryInfo.unitCost || 0 }}</span>
</el-form-item>
<el-form-item label="总成本">
<el-form-item label="成本(元)">
<span>{{ inventoryInfo.totalCost || 0 }}</span>
</el-form-item>
</div>
......@@ -136,7 +136,7 @@ const closeDialog = () => {
}
.location-info {
flex: 0 0 400px;
flex: 0 0 600px;
}
.visual-column h3 {
......
......@@ -67,18 +67,18 @@
<!-- 是否显示0库存 -->
<el-form-item label="是否显示0库存">
<el-select v-model="queryParams.showzerostock" placeholder="请选择" clearable >
<el-option label="显示" value="true" />
<el-option label="不显示" value="false" />
<el-option label="显示" :value="true" />
<el-option label="不显示" :value="false" />
</el-select>
</el-form-item>
<!-- 排序方式 -->
<el-form-item label="排序方式">
<el-select v-model="queryParams.orderBy" placeholder="请选择" clearable >
<el-option label="总库存降序" value="totalquantity DESC" />
<el-option label="总库存升序" value="totalquantity ASC" />
<el-option label="总金额降序" value="totalAmount DESC" />
<el-option label="总金额升序" value="totalAmount ASC" />
<el-option label="总库存降序" value="totalQuantity DESC" />
<el-option label="总库存升序" value="totalQuantity ASC" />
<el-option label="总金额降序" value="totalCost DESC" />
<el-option label="总金额升序" value="totalCost ASC" />
</el-select>
</el-form-item>
......@@ -96,16 +96,18 @@
<template #default="props">
<el-table :data="props.row.inventoryList" border style="width: 100%" size="small">
<el-table-column prop="batchNo" label="批次号" width="150" />
<el-table-column prop="warehouse.warehouseName" label="仓库" />
<el-table-column prop="area.areaName" label="库区" />
<el-table-column prop="location.locationName" label="货架"/>
<el-table-column label="仓库/库区/货架">
<template #default="scope">
<div>{{ scope.row.warehouse.warehouseName || '-' }} / {{ scope.row.area.areaName || '-' }}/ {{ scope.row.location.locationName || '-' }}</div>
</template>
</el-table-column>
<el-table-column label="批次号" align="center" prop="batchNo" width="180" />
<el-table-column label="生产日期" align="center" prop="productionDate" width="180">
<el-table-column label="生产日期" align="center" prop="productionDate">
<template #default="scope">
<span>{{ parseTime(scope.row.productionDate, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="有效日期" align="center" prop="expirationDate" width="180">
<el-table-column label="有效日期" align="center" prop="expirationDate">
<template #default="scope">
<span>{{ parseTime(scope.row.expirationDate, '{y}-{m}-{d}') }}</span>
</template>
......@@ -113,19 +115,19 @@
<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="totalCost" />
<el-table-column label="入库单价(元)" align="center" prop="unitCost" />
<el-table-column label="成本(元)" align="center" prop="totalCost" />
<el-table-column label="库存状态" align="center" prop="inventoryStatus">
<template #default="scope">
<dict-tag :options="inventory_status" :value="scope.row.inventoryStatus" />
</template>
</el-table-column>
<el-table-column label="最后入库时间" align="center" prop="lastInboundTime" width="180">
<el-table-column label="最后入库时间" align="center" prop="lastInboundTime" >
<template #default="scope">
<span>{{ parseTime(scope.row.lastInboundTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="最后出库时间" align="center" prop="lastOutboundTime" width="180">
<el-table-column label="最后出库时间" align="center" prop="lastOutboundTime">
<template #default="scope">
<span>{{ parseTime(scope.row.lastOutboundTime, '{y}-{m}-{d}') }}</span>
</template>
......@@ -135,7 +137,6 @@
<span>{{ scope.row.rfidTag || '-' }}</span>
</template>
</el-table-column>
<el-table-column label="RFID绑定数量" align="center" prop="rfidBindingCount" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="180" fixed="right">
<template #default="scope">
<el-button link type="primary" icon="Document" @click="handleFlowQuery(scope.row)" v-hasPermi="['ware:wmsInventory:flow']">库存履历</el-button>
......@@ -153,7 +154,7 @@
<el-table-column prop="specification" label="规格型号" width="150" />
<el-table-column prop="unit" label="计量单位" width="100" />
<el-table-column prop="totalQuantity" label="总库存" />
<!-- <el-table-column prop="totalAmount" label="总金额" /> -->
<el-table-column prop="totalCost" label="总成本(元)" />
<el-table-column prop="locationCount" label="存储货架数" />
<el-table-column prop="batchCount" label="批次数量"/>
</el-table>
......@@ -187,6 +188,7 @@ import { listWmsArea } from '@/api/ware/wmsArea'
import { ElMessage } from 'element-plus'
import LocationVisualDialog from './components/LocationVisualDialog.vue'
import InventoryFlowDialog from '@/components/InventoryFlowDialog.vue'
import { listWmsLocation } from '@/api/ware/wmsLocation'
const { proxy } = getCurrentInstance()
const { sys_yes_no, inventory_status } = proxy.useDict('sys_yes_no', 'inventory_status')
......@@ -206,8 +208,8 @@ const queryParams = reactive({
materialName: null,
minQuantity: null,
maxQuantity: null,
showzerostock: null,
orderBy: 'totalquantity DESC'
showzerostock: false,
orderBy: 'totalQuantity DESC'
})
// 物资分类选项
......@@ -263,7 +265,7 @@ const resetQuery = () => {
queryParams.minQuantity = null
queryParams.maxQuantity = null
queryParams.showzerostock = null
queryParams.orderBy = 'totalquantity DESC'
queryParams.orderBy = 'totalQuantity DESC'
queryParams.pageNum = 1
// 重置库区列表
......@@ -373,25 +375,21 @@ const handleLocationVisualization = (row) => {
// 设置当前位置信息
currentLocation.value = {
locationId: row.location.locationId,
locationName: row.location.locationName,
locationCode: row.location.locationCode
locationId: row.locationId,
locationName: row.location?.locationName,
}
// 获取所有相关位置信息(这里需要根据实际情况调整,可能需要调用API)
// 暂时使用模拟数据
locations.value = [
{
locationId: row.location.locationId,
locationName: row.location.locationName,
locationCode: row.location.locationCode
}
]
// 显示对话框
locationVisualVisible.value = true
}
const getLocations = () => {
// 获取所有货架列表
listWmsLocation({ pageNum: 1, pageSize: 1000 }).then(response => {
locations.value = response.rows;
})
}
// 库存履历
const handleFlowQuery = (row) => {
// 设置选中的库存ID
......@@ -406,6 +404,7 @@ onMounted(() => {
initWarehouseList()
initCategoryTree()
getInventoryList()
getLocations()
})
</script>
......
......@@ -24,14 +24,16 @@
</div>
<el-table ref="tableRef" :data="orderDetails" stripe highlight-current-row row-key="itemId"
height="calc(100vh - 320px)" v-loading="loadingDetails" @expand-change="handleExpandChange" preserve-expanded-content>
height="calc(100vh - 320px)" v-loading="loadingDetails" @expand-change="handleExpandChange"
preserve-expanded-content>
<el-table-column type="expand">
<template #default="props">
<el-table :data="props.row.inventoryRecords">
<el-table :data="props.row.inventoryRecords" height="400px">
<el-table-column label="批次号" align="center" prop="batchNo" width="120" />
<el-table-column label="仓库/库区/货架" align="center">
<template #default="scope">
{{ scope.row.warehouse?.warehouseName || '-' }}/{{ scope.row.area?.areaName || '-' }}/{{ scope.row.location?.locationName || '-' }}
{{ scope.row.warehouse?.warehouseName || '-' }}/{{ scope.row.area?.areaName || '-' }}/{{
scope.row.location?.locationName || '-' }}
</template>
</el-table-column>
<el-table-column label="RFID标签" align="center" prop="rfidCode" />
......@@ -55,7 +57,8 @@
<!-- <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 v-if="scope.row.isCanceled === '0'" type="primary" @click="handleCancelByRelation(scope.row)">撤销</el-button>
<el-button link v-if="scope.row.isCanceled === '0'" type="primary"
@click="handleCancelByRelation(scope.row)">撤销</el-button>
</template>
</el-table-column>
</el-table>
......@@ -73,11 +76,22 @@
<dict-tag :options="out_order_status" :value="scope.row.pickingStatus" />
</template>
</el-table-column>
<el-table-column label="操作" width="100" align="center" fixed="right">
<el-table-column label="操作" width="120" align="center" fixed="right">
<template #default="scope">
<el-button v-if="scope.row.pickingStatus !== '0'" type="primary" size="small" @click="handleCancel(scope.row)">
撤销
</el-button>
<div>
<el-button v-if="scope.row.pickingStatus !== '0'" type="primary" size="small"
@click="handleCancel(scope.row)">
撤销
</el-button>
</div>
<div>
<el-button v-if="scope.row.pickingStatus !== '2'" type="success" size="small" @click="showBatchRecommendation(scope.row)">
<el-icon>
<MagicStick />
</el-icon>
智能推荐批次
</el-button>
</div>
</template>
</el-table-column>
</el-table>
......@@ -90,18 +104,13 @@
<!-- <el-icon><Scan /></el-icon> -->
扫码出库
</h3>
<el-button type="info" size="small" @click="showBatchRecommendation">
<el-icon>
<MagicStick />
</el-icon>
智能推荐批次
</el-button>
</div>
<!-- 扫码输入区域 -->
<div class="scan-input-area">
<div class="input-wrapper">
<el-input v-model="scanInput" placeholder="请扫描二维码" size="large" clearable @keyup.enter="handleScan"
<el-input v-model="scanInput" placeholder="请扫描RFID标签" size="large" clearable @keyup.enter="handleScan"
@clear="clearScanResult" ref="scanInputRef">
<template #prepend>
<el-icon>
......@@ -240,7 +249,7 @@
{{ row.message }}
</template>
</el-table-column>
<el-table-column prop="locationId" label="货架" width="100" />
<!-- <el-table-column prop="locationId" label="货架" width="100" /> -->
</el-table>
</div>
</div>
......@@ -250,8 +259,8 @@
<BatchDetailDialog v-model="showBatchDialog" :detail="selectedDetail" :transactions="detailTransactions" />
<!-- 批次推荐对话框 -->
<BatchRecommendationDialog v-model="showRecommendationDialog" :detail="selectedDetail"
@select-batch="handleSelectRecommendedBatch" />
<BatchRecommendationDialog ref="recommendationDialogRef" v-model="showRecommendationDialog" :detail="selectedDetail"
@select-batch="handleSelectRecommendedBatch" @select-batch-all="handleSelectAllRecommendations" />
</div>
</el-dialog>
</template>
......@@ -282,7 +291,7 @@ import { updateWmsOutboundOrder, confirmOutbound } from '@/api/ware/wmsOutboundO
import useUserStore from '@/store/modules/user'
import { cancelByDetailId, cancelByRelationId } from '@/api/ware/wmsOutboundOrder'
import { listWmsInventory } from "@/api/ware/wmsInventory"
import {listWmsOutboundItemInventory} from "@/api/ware/wmsOutboundItemInventory"
import { listWmsOutboundItemInventory } from "@/api/ware/wmsOutboundItemInventory"
const { proxy } = getCurrentInstance()
......@@ -334,7 +343,7 @@ const loadingTransactions = ref(false)
// 对话框控制
const showBatchDialog = ref(false)
const showRecommendationDialog = ref(false)
const recommendationDialogRef = ref(null)
// 计算属性
const totalPlanned = computed(() => {
return orderDetails.value.reduce((sum, detail) => sum + (detail.planQuantity || 0), 0)
......@@ -391,7 +400,7 @@ const loadOutboundItemInventory = async (row) => {
pageNum: 1,
pageSize: 1000
})
// 直接更新传入的row对象,保持对象引用不变
row.inventoryRecords = response.rows || []
......@@ -489,8 +498,6 @@ const handleOutbound = async () => {
lastTransaction.value = confirmResponse.data?.relation;
tableRef.value?.toggleRowExpansion(currentDetail, true)
// 检查当前行是否已展开
if (expandedRows.value.has(currentDetail.itemId)) {
// 先收缩行
......@@ -499,6 +506,8 @@ const handleOutbound = async () => {
nextTick(() => {
tableRef.value?.toggleRowExpansion(currentDetail, true)
})
}else{
tableRef.value?.toggleRowExpansion(currentDetail, true)
}
getWmsOutboundOrderItem(currentDetail.itemId).then(res => {
......@@ -510,7 +519,7 @@ const handleOutbound = async () => {
showScanMessage(`出库成功: ${outboundQuantity.value} ${currentBatch.value.unit}`, 'success')
// 清空当前批次
clearScanResult()
......@@ -562,7 +571,7 @@ const handleUndoLast = async () => {
currentDetail.actualQuantity = (currentDetail.actualQuantity || 0) - (lastRecord.quantity || 0);
currentDetail.pickingStatus = currentDetail.actualQuantity === 0 ? '0' : '1'
// 检查当前行是否已展开
// 检查当前行是否已展开
if (expandedRows.value.has(currentDetail.itemId)) {
// 先收缩行
tableRef.value?.toggleRowExpansion(currentDetail, false)
......@@ -581,21 +590,16 @@ const handleUndoLast = async () => {
}
}
const showBatchRecommendation = async () => {
// if (!selectedDetail.value) {
// const detail = orderDetails.value.find(d => d.id === currentDetailId.value)
// if (!detail) {
// ElMessage.warning('请先选择需要出库的明细行')
// return
// }
// selectedDetail.value = detail
// }
const showBatchRecommendation = async (row) => {
row.warehouseId = props.order.warehouseId
selectedDetail.value = row
showRecommendationDialog.value = true
recommendationDialogRef.value?.open(row)
}
const handleSelectRecommendedBatch = (batch) => {
currentBatch.value = batch
scanInput.value = batch.batchNo
scanInput.value = batch.rfidTag
showRecommendationDialog.value = false
// 自动触发扫描
nextTick(() => {
......@@ -603,6 +607,11 @@ const handleSelectRecommendedBatch = (batch) => {
})
}
const handleSelectAllRecommendations = (batches) => {
}
const clearScanResult = () => {
currentBatch.value = null
outboundQuantity.value = 0
......@@ -660,7 +669,7 @@ const handleCancel = async (row) => {
}
ElMessage.success('已撤销成功')
emit('order-updated')
// 检查当前行是否已展开
......@@ -691,7 +700,7 @@ const handleCancelByRelation = async (row) => {
}
ElMessage.success('已撤销成功')
const currentRow = orderDetails.value.find(d => d.itemId === row.itemId)
getWmsOutboundOrderItem(currentRow.itemId).then(res => {
......
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="120px">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="80px">
<el-form-item label="出库单号" prop="orderNo">
<el-input
v-model="queryParams.orderNo"
......
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