Commit c2162be0 by wangchunyang
parents b1240733 9e20d976
<template>
<div class="material-selector-container" style="overflow: hidden;">
<splitpanes class="default-theme">
<!-- 左侧分类树 -->
<pane size="16" style="overflow: auto;">
<TreeComponent
ref="treeComponent"
:tree-data="categoryTreeData"
:tree-props="treeProps"
:node-key="nodeKey"
:show-search="true"
search-placeholder="请输入分类名称"
:default-expand-all="true"
:highlight-current="true"
:loading="loadingTree"
@node-click="handleTreeClick"
>
<template #node-content="{ node, data }">
<span class="custom-tree-node">{{ node.label }}</span>
</template>
</TreeComponent>
</pane>
<!-- 右侧物料列表 -->
<pane size="84" style="overflow: auto;">
<div style="padding: 10px; display: flex; flex-direction: column;">
<!-- 查询表单 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" label-width="88px">
<el-form-item label="SAP物料号" prop="sapNo">
<el-input
v-model="queryParams.sapNo"
placeholder="请输入SAP物料号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="物料名称" prop="materialName">
<el-input
v-model="queryParams.materialName"
placeholder="请输入物料名称"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="物料分类" prop="categoryNameInput">
<el-input
v-model="queryParams.categoryNameInput"
placeholder="请输入物料分类"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 物料表格:修复选择列逻辑,避免锁定 -->
<el-table
ref="materialTable"
v-loading="loading"
:data="materialsList"
@selection-change="handleSelectionChange"
:scroll-x="true"
:row-key="row => row.id"
@row-click="handleRowClick"
:select-on-indeterminate="false"
@select="handleTableSelect"
>
<!-- 修复单选/多选列:单选模式下不限制selectable,通过事件控制唯一选中 -->
<el-table-column
type="selection"
width="55"
align="center"
/>
<el-table-column type="index" label="序号" align="center"/>
<el-table-column label="SAP物料号" align="center" prop="sapNo" />
<el-table-column label="物料名称" align="center" prop="materialName" width="150"/>
<el-table-column label="TS Code" align="center" prop="tsCode" />
<el-table-column label="物料分类" align="center" prop="categoryCode">
<template slot-scope="scope">
{{ scope.row.displayCategory || categoryMap[scope.row.categoryCode] || scope.row.categoryCode || '-' }}
</template>
</el-table-column>
<el-table-column label="规格型号" align="center" prop="specification" />
<el-table-column label="计量单位" align="center" prop="materialUnit" />
<el-table-column label="是否批次管理" align="center" prop="isBatchManaged">
<template slot-scope="scope">
<el-tag :type="scope.row.isBatchManaged === 1 ? 'success' : 'info'" size="mini">
{{ scope.row.isBatchManaged === 1 ? '是' : '否' }}
</el-tag>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
</div>
</pane>
</splitpanes>
</div>
</template>
<script>
import { listMaterials } from "@/api/inventory/materials"
import { listMaterials_category } from "@/api/inventory/materials_category"
import TreeComponent from '@/views/inventory/materials_category/treeComponent.vue'
import { Splitpanes, Pane } from 'splitpanes'
import 'splitpanes/dist/splitpanes.css'
export default {
name: "MaterialSelector",
components: { TreeComponent, Splitpanes, Pane },
props: {
// 支持传入默认选中的物料ID(单选传单个ID,多选传数组)
value: {
type: [Array, Number, String],
default: () => []
},
// 是否允许多选
multiple: {
type: Boolean,
default: true
},
// 默认选中的分类编码
defaultCategoryCodes: {
type: Array,
default: () => []
}
},
data() {
return {
// 分类树相关
categoryTreeData: [],
treeProps: { children: 'children', label: 'label', value: 'sid' },
nodeKey: 'sid',
loadingTree: false,
categoryMap: {}, // 分类编码->分类名称
categoryNameToCodeMap: {}, // 分类名称->分类编码
categoryCodeToSidMap: {}, // 分类编码->树节点sid
currentNodeId: null,
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
sapNo: null,
materialName: null,
tsCode: null,
categoryCode: null,
categoryNameInput: null,
specification: null,
},
// 物料列表相关
materialsList: [],
total: 0,
loading: false,
selectedRows: [], // 选中的物料行数据
singleSelectedId: null, // 单选模式下的选中ID
isSelecting: false // 防止选择事件重复触发的锁
}
},
watch: {
// 监听外部传入的选中值变化
value: {
immediate: true,
deep: true,
handler(val) {
// 防止加载中或正在选择时触发,避免锁定
if (this.loading || this.isSelecting) return
if (!val || (Array.isArray(val) && !val.length)) {
this.clearSelection()
return
}
this.$nextTick(() => {
if (!this.$refs.materialTable || !this.materialsList.length) return
// 统一处理值格式:单选转单个ID,多选转数组
const targetIds = this.multiple
? Array.isArray(val) ? val.map(id => Number(id)) : [Number(val)]
: [Array.isArray(val) ? Number(val[0]) : Number(val)]
this.isSelecting = true // 加锁,防止重复触发
try {
// 清空原有选择
this.$refs.materialTable.clearSelection()
if (this.multiple) {
// 多选模式:选中所有匹配的行
this.materialsList.forEach(row => {
if (targetIds.includes(Number(row.id))) {
this.$refs.materialTable.toggleRowSelection(row, true)
}
})
} else {
// 单选模式:只选中第一个匹配的行
this.singleSelectedId = targetIds[0] || null
const targetRow = this.materialsList.find(row => Number(row.id) === this.singleSelectedId)
if (targetRow) {
this.$refs.materialTable.toggleRowSelection(targetRow, true)
}
}
} finally {
this.isSelecting = false // 解锁
}
})
}
},
// 监听多选状态切换:彻底重置选择状态,避免锁定
multiple: {
immediate: true,
handler(val) {
if (this.$refs.materialTable) {
this.isSelecting = true
try {
// 清空所有选择状态
this.$refs.materialTable.clearSelection()
this.selectedRows = []
this.singleSelectedId = null
// 重新初始化选中状态
this.$nextTick(() => {
this.$watchers.find(w => w.expression === 'value').handler(this.value)
})
} finally {
this.isSelecting = false
}
}
}
},
// 监听默认分类编码变化
defaultCategoryCodes: {
immediate: true,
handler(val) {
if (val && val.length && this.categoryTreeData.length) {
this.$nextTick(() => {
this.selectCategoryNodes(val)
})
}
}
}
},
async created() {
// 并行加载分类数据和分类树
await Promise.all([
this.getCategoryList(),
this.getCategoryTreeData()
])
// 加载物料列表
this.getList()
},
methods: {
/**
* 获取分类列表,构建编码-名称映射
*/
async getCategoryList() {
try {
const response = await listMaterials_category({ pageNum: 1, pageSize: 1000 })
if (response.rows && response.rows.length > 0) {
this.categoryMap = {}
this.categoryNameToCodeMap = {}
response.rows.forEach(item => {
// 只处理启用的分类
if (item.isUsed !== 0 && item.isUsed !== '0') {
const code = item.categoryCode
this.categoryMap[code] = item.categoryName
// 构建分类名称到编码的映射(处理同名分类)
if (!this.categoryNameToCodeMap[item.categoryName]) {
this.categoryNameToCodeMap[item.categoryName] = code
} else if (!Array.isArray(this.categoryNameToCodeMap[item.categoryName])) {
this.categoryNameToCodeMap[item.categoryName] = [this.categoryNameToCodeMap[item.categoryName], code]
} else {
this.categoryNameToCodeMap[item.categoryName].push(code)
}
}
})
}
} catch (error) {
console.error('获取分类列表失败:', error)
}
},
/**
* 获取分类树数据
*/
async getCategoryTreeData() {
this.loadingTree = true
try {
const response = await listMaterials_category({ pageNum: 1, pageSize: 1000 })
if (response.rows && response.rows.length > 0) {
// 过滤启用的分类
const activeCategories = response.rows.filter(item => item.isUsed !== 0 && item.isUsed !== '0')
// 构建树形结构
this.categoryTreeData = this.buildTreeData(activeCategories)
// 构建分类编码到树节点sid的映射
this.buildCategoryCodeToSidMap(this.categoryTreeData)
}
} catch (error) {
console.error('获取分类树数据失败:', error)
} finally {
this.loadingTree = false
}
},
/**
* 构建分类树形结构
*/
buildTreeData(list, parentId = null) {
return list
.filter(item => parentId === null
? (!item.parentId || item.parentId === 0 || item.parentId === '0')
: Number(item.parentId) === Number(parentId)
)
.map(item => ({
...item,
sid: String(item.id),
label: item.categoryName,
children: this.buildTreeData(list, item.id).length
? this.buildTreeData(list, item.id)
: undefined
}))
},
/**
* 构建分类编码到树节点sid的映射
*/
buildCategoryCodeToSidMap(treeData) {
treeData.forEach(node => {
if (node.categoryCode) {
this.categoryCodeToSidMap[node.categoryCode] = node.sid
// 兼容格式化的编码(如带横线的)
const rawCode = node.categoryCode.replace(/-/g, '')
if (rawCode !== node.categoryCode) {
this.categoryCodeToSidMap[rawCode] = node.sid
}
}
if (node.children && node.children.length) {
this.buildCategoryCodeToSidMap(node.children)
}
})
},
/**
* 根据分类编码选中树节点
*/
selectCategoryNodes(categoryCodes) {
if (!this.$refs.treeComponent || !this.$refs.treeComponent.$refs.tree) return
const tree = this.$refs.treeComponent.$refs.tree
categoryCodes.forEach(code => {
const rawCode = code.replace(/-/g, '')
const sid = this.categoryCodeToSidMap[rawCode] || this.categoryCodeToSidMap[code]
if (sid) {
tree.setCurrentKey(sid)
this.currentNodeId = sid
// 展开节点
const node = tree.getNode(sid)
if (node) {
tree.expandNode(node)
}
}
})
},
/**
* 分类树节点点击事件
*/
handleTreeClick(data) {
this.currentNodeId = data.sid
this.queryParams.categoryCode = data.categoryCode
this.queryParams.categoryNameInput = null
this.queryParams.pageNum = 1
this.getList()
},
/**
* 获取物料列表数据
*/
getList() {
this.loading = true
listMaterials(this.queryParams).then(response => {
// 过滤启用的物料并处理分类名称显示
this.materialsList = (response.rows || []).filter(item => item.isUsed !== 0 && item.isUsed !== '0').map(item => ({
...item,
displayCategory: this.categoryMap[item.categoryCode] || `${item.categoryCode}(未匹配分类)`
}))
this.total = response.total || 0
// 列表加载完成后重新同步选中状态(延迟执行,避免DOM未更新)
setTimeout(() => {
this.$watchers.find(w => w.expression === 'value').handler(this.value)
}, 100)
}).catch(error => {
console.error('获取物料列表失败:', error)
this.materialsList = []
this.total = 0
}).finally(() => {
this.loading = false
})
},
/**
* 搜索按钮点击事件
*/
handleQuery() {
// 根据分类名称匹配分类编码
const inputName = this.queryParams.categoryNameInput
if (inputName) {
const matchedCode = this.categoryNameToCodeMap[inputName]
if (matchedCode) {
this.queryParams.categoryCode = Array.isArray(matchedCode) ? matchedCode[0] : matchedCode
} else {
// 模糊匹配分类名称
const matchedCodes = Object.entries(this.categoryMap)
.filter(([code, name]) => name.includes(inputName))
.map(([code]) => code)
if (matchedCodes.length > 0) {
this.queryParams.categoryCode = matchedCodes[0]
}
}
}
this.queryParams.pageNum = 1
this.getList()
},
/**
* 重置查询条件
*/
resetQuery() {
this.queryParams = {
pageNum: 1,
pageSize: 10,
sapNo: null,
materialName: null,
tsCode: null,
categoryCode: null,
categoryNameInput: null,
specification: null,
}
this.currentNodeId = null
// 清空树节点选中状态
if (this.$refs.treeComponent && this.$refs.treeComponent.$refs.tree) {
this.$refs.treeComponent.$refs.tree.setCurrentKey(null)
}
this.getList()
},
/**
* 表格选择变化事件(批量选择)
*/
handleSelectionChange(selection) {
if (this.isSelecting || !this.$refs.materialTable) return
this.isSelecting = true
try {
// 单选模式处理:确保只有一个选中项
if (!this.multiple) {
if (selection.length > 1) {
// 移除多余选中项,只保留最后一个
const lastRow = selection.pop()
this.$refs.materialTable.clearSelection()
this.$refs.materialTable.toggleRowSelection(lastRow, true)
this.selectedRows = [lastRow]
this.singleSelectedId = Number(lastRow.id)
} else {
this.selectedRows = selection
this.singleSelectedId = selection.length > 0 ? Number(selection[0].id) : null
}
} else {
// 多选模式
this.selectedRows = selection
}
// 组装选中数据并触发事件
const selectedIds = this.selectedRows.map(row => Number(row.id))
const selectedData = this.selectedRows.map(item => ({
id: Number(item.id),
sapNo: item.sapNo,
materialName: item.materialName,
categoryCode: item.categoryCode,
specification: item.specification,
materialUnit: item.materialUnit,
isBatchManaged: item.isBatchManaged
}))
// 触发事件:input用于v-model绑定,change返回详细数据
if (this.multiple) {
this.$emit('input', selectedIds)
this.$emit('change', selectedData)
} else {
const singleId = selectedIds.length > 0 ? selectedIds[0] : ''
const singleData = selectedData.length > 0 ? selectedData[0] : null
this.$emit('input', singleId)
this.$emit('change', singleData)
}
} finally {
this.isSelecting = false
}
},
/**
* 表格单个选择事件(点击选择框)
*/
handleTableSelect(selection, row) {
if (this.isSelecting || this.multiple) return
// 单选模式:点击选择框时,清空其他选中项
this.isSelecting = true
try {
const isSelected = selection.includes(row)
this.$refs.materialTable.clearSelection()
if (isSelected) {
this.$refs.materialTable.toggleRowSelection(row, true)
this.singleSelectedId = Number(row.id)
this.selectedRows = [row]
this.$emit('input', Number(row.id))
this.$emit('change', row)
} else {
this.singleSelectedId = null
this.selectedRows = []
this.$emit('input', '')
this.$emit('change', null)
}
} finally {
this.isSelecting = false
}
},
/**
* 表格行点击事件
*/
handleRowClick(row) {
if (this.isSelecting || !this.$refs.materialTable) return
this.isSelecting = true
try {
if (!this.multiple) {
// 单选模式:点击行切换选中状态
const isCurrentlySelected = Number(this.singleSelectedId) === Number(row.id)
this.$refs.materialTable.clearSelection()
if (!isCurrentlySelected) {
this.$refs.materialTable.toggleRowSelection(row, true)
this.singleSelectedId = Number(row.id)
this.selectedRows = [row]
this.$emit('input', Number(row.id))
this.$emit('change', row)
} else {
this.singleSelectedId = null
this.selectedRows = []
this.$emit('input', '')
this.$emit('change', null)
}
} else {
// 多选模式:点击行切换选中状态
this.$refs.materialTable.toggleRowSelection(row)
}
} finally {
this.isSelecting = false
}
},
/**
* 清空选中状态(外部调用)
*/
clearSelection() {
if (this.isSelecting || !this.$refs.materialTable) return
this.isSelecting = true
try {
this.$refs.materialTable.clearSelection()
this.selectedRows = []
this.singleSelectedId = null
this.$emit('input', this.multiple ? [] : '')
this.$emit('change', this.multiple ? [] : null)
} finally {
this.isSelecting = false
}
},
/**
* 获取选中的物料详情(外部调用)
*/
getSelectedMaterials() {
return this.multiple ? [...this.selectedRows] : (this.selectedRows[0] || null)
},
/**
* 设置单选选中项(外部调用)
*/
setSingleSelection(materialId) {
if (this.isSelecting || this.multiple) return
this.isSelecting = true
try {
const targetId = Number(materialId)
this.singleSelectedId = targetId
this.$nextTick(() => {
this.$refs.materialTable.clearSelection()
const targetRow = this.materialsList.find(row => Number(row.id) === targetId)
if (targetRow) {
this.$refs.materialTable.toggleRowSelection(targetRow, true)
this.selectedRows = [targetRow]
this.$emit('input', targetId)
this.$emit('change', targetRow)
}
})
} finally {
this.isSelecting = false
}
}
}
}
</script>
<style scoped>
.material-selector-container {
height: 100%;
min-height: 500px;
}
.custom-tree-node {
font-size: 14px;
}
/* 优化表格选择框样式,避免点击区域冲突 */
/deep/ .el-table .el-table__header .cell {
text-align: center;
}
/deep/ .el-table--enable-row-hover .el-table__body tr:hover>td {
background-color: #f5f7fa;
}
/* 确保选择框可点击,无遮挡 */
/deep/ .el-table__fixed-right,
/deep/ .el-table__fixed-left {
pointer-events: auto !important;
}
</style>
\ No newline at end of file
......@@ -74,7 +74,6 @@
align="center"
/>
<el-table-column type="index" label="序号" align="center"/>
<el-table-column label="物料ID" align="center" prop="id" />
<el-table-column label="SAP物料号" align="center" prop="sapNo" />
<el-table-column label="物料名称" align="center" prop="materialName" width="150"/>
<el-table-column label="TS Code" align="center" prop="tsCode" />
......
......@@ -113,7 +113,6 @@
/>
</el-select>
</el-form-item>
<!-- 新增查询字段 -->
<el-form-item label="库位使用" prop="locationUsage">
<el-select
v-model="queryParams.locationUsage"
......@@ -188,7 +187,6 @@
<div><strong>层:</strong>{{ scope.row.layerCode || '-' }}</div>
</el-col>
</el-row>
<!-- 新增展开字段 -->
<el-row :gutter="20" style="margin: 10px 0;">
<el-col :span="6">
<div><strong>上架顺序:</strong>{{ scope.row.putawayOrder || '-' }}</div>
......@@ -241,7 +239,6 @@
<dict-tag :options="dict.type.location_type" :value="scope.row.locationType"/>
</template>
</el-table-column>
<!-- 新增表格列 -->
<el-table-column label="库位使用" align="center" prop="locationUsage" width="100">
<template slot-scope="scope">
<dict-tag :options="dict.type.location_usage" :value="scope.row.locationUsage"/>
......@@ -271,7 +268,7 @@
>
<div class="category-tags">
<el-tag
v-for="(name, index) in (scope.row.allowedCategoryNames ? scope.row.allowedCategoryNames.split(',') : [])"
v-for="(name, index) in (scope.row.allowedCategoryNames ? scope.row.allowedCategoryNames.split(',').filter(n => n.trim()) : [])"
:key="name"
type="info"
size="small"
......@@ -281,12 +278,12 @@
{{ name }}
</el-tag>
<el-tag
v-if="(scope.row.allowedCategoryNames ? scope.row.allowedCategoryNames.split(',').length : 0) > 3"
v-if="(scope.row.allowedCategoryNames ? scope.row.allowedCategoryNames.split(',').filter(n => n.trim()).length : 0) > 3"
type="info"
size="small"
style="margin-right: 5px; margin-bottom: 5px; background: #f0f9eb; border-color: #c2e7b0;"
>
+{{ (scope.row.allowedCategoryNames ? scope.row.allowedCategoryNames.split(',').length : 0) - 3 }}
+{{ (scope.row.allowedCategoryNames ? scope.row.allowedCategoryNames.split(',').filter(n => n.trim()).length : 0) - 3 }}
</el-tag>
</div>
</el-tooltip>
......@@ -595,7 +592,7 @@
</div>
</el-dialog>
<!-- 导入组件 - 集成货主页面的导入功能 -->
<!-- 导入组件 -->
<import-excel
ref="import"
title="库位导入"
......@@ -624,7 +621,6 @@ import { listMaterials } from "@/api/inventory/materials"
export default {
name: "Locations",
components: { materialsSeletor, WarehouseSelector, ImportExcel },
// 新增字典类型
dicts: ['sys_normal_disable', 'location_type', 'location_usage', 'yorn'],
data() {
return {
......@@ -638,48 +634,28 @@ export default {
title: "",
open: false,
dict: {
type: {
location_type: [
{ label: "货架", value: "1" },
{ label: "地面", value: "2" },
{ label: "货位", value: "3" },
{ label: "专区", value: "4" }
],
sys_normal_disable: [
{ label: "启用", value: "1" },
{ label: "禁用", value: "0" }
],
// 新增字典默认值(实际会从后端加载)
location_usage: [],
yorn: [
{ label: "是", value: "Y" },
{ label: "否", value: "N" }
]
}
},
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
locationCode: null,
locationName: null,
warehouseId: null, // 前端保持warehouseId,内部映射后端的warehousesId
warehouseId: null,
locationType: null,
layerCode: null,
// 新增查询参数
locationUsage: null,
allowMixedProducts: null,
zoneCode: null,
pickingArea: null
},
// 表单数据
form: {
id: null,
locationCode: null,
locationName: null,
warehouseId: null, // 前端保持warehouseId,内部映射后端的warehousesId
warehouseName: null, // 新增仓库名称字段
warehouseId: null,
warehouseName: null,
locationType: null,
zoneCode: null,
rowCode: null,
......@@ -696,7 +672,6 @@ export default {
isEnabled: 1,
isUsed: 1,
sortNo: 0,
// 新增表单字段
putawayOrder: null,
pickingOrder: null,
locationUsage: null,
......@@ -707,6 +682,7 @@ export default {
allowMixedBatches: null
},
// 表单验证规则
rules: {
locationCode: [
{ required: true, message: '库位编码不能为空', trigger: 'blur' },
......@@ -716,28 +692,32 @@ export default {
{ required: true, message: '库位名称不能为空', trigger: 'blur' },
{ min: 1, max: 100, message: '库位名称长度不能超过100个字符', trigger: 'blur' }
],
warehouseId: [ // 验证规则保持warehouseId
warehouseId: [
{ required: true, message: '仓库不能为空', trigger: 'change' }
],
locationType: [
{ required: true, message: '库位类型不能为空', trigger: 'change' }
],
// 新增表单验证规则
locationUsage: [
{ required: true, message: '库位使用不能为空', trigger: 'change' }
],
allowMixedProducts: [
{ required: true, message: '允许混放产品不能为空', trigger: 'change' }
],
allowMixedBatches: [
{ required: true, message: '允许混放批次不能为空', trigger: 'change' }
],
isEnabled: [
{ required: true, message: '应用状态不能为空', trigger: 'change' }
]
},
warehouseList: [], // 仓库列表(包含id和名称)
// 仓库相关
warehouseList: [],
loadingWarehouse: false,
queryWarehouseName: '', // 查询用仓库名称
queryWarehouseName: '',
// 物料选择相关
showMaterialSelect: false,
tempSelectedMaterials: {
materialCodes: [],
......@@ -746,12 +726,15 @@ export default {
},
materialCodeToNameMap: {},
materialMapLoaded: false,
// 仓库选择器相关
warehouseSelectorVisible: false,
warehouseSelectTarget: '', // 仓库选择目标(query/form)
currentDetailItem: null // 当前编辑的明细项
warehouseSelectTarget: '',
currentDetailItem: null
}
},
created() {
console.log('【Locations组件】开始初始化')
this.getList()
this.getWarehouseList()
this.initMaterialCodeToNameMap()
......@@ -759,6 +742,7 @@ export default {
methods: {
// 清空查询仓库
clearQueryWarehouse() {
console.log('【清空查询仓库】执行清空操作')
this.queryWarehouseName = ''
this.queryParams.warehouseId = null
this.handleQuery()
......@@ -766,14 +750,15 @@ export default {
// 清空表单仓库
clearFormWarehouse() {
console.log('【清空表单仓库】执行清空操作')
this.form.warehouseName = ''
this.form.warehouseId = null
},
// 仓库选择回调 - 适配后端返回的warehousesId
// 仓库选择回调
handleWarehouseSelected(warehouse) {
console.log('【仓库选择回调】选中的仓库数据:', warehouse)
if (!warehouse) return
// 统一处理:优先取warehousesId,兼容warehouseId/id
const warehouseId = warehouse.warehousesId || warehouse.warehouseId || warehouse.id
const warehouseName = warehouse.warehousesName || warehouse.name || warehouse.warehouseName
......@@ -785,7 +770,6 @@ export default {
this.form.warehouseId = warehouseId
this.form.warehouseName = warehouseName
// 更新明细中的默认仓库
if (this.currentDetailItem) {
this.currentDetailItem.warehouseId = warehouseId
this.currentDetailItem.warehouseName = warehouseName
......@@ -796,35 +780,41 @@ export default {
// 打开仓库选择器
openWarehouseSelector(target = 'form') {
console.log('【打开仓库选择器】目标:', target)
this.warehouseSelectTarget = target
this.warehouseSelectorVisible = true
},
// 获取仓库列表(包含id)
// 获取仓库列表
getWarehouseList() {
console.log('【获取仓库列表】开始请求仓库数据')
this.loadingWarehouse = true
listWarehouses({ pageNum: 1, pageSize: 1000, isUsed: 1 }).then(response => {
console.log('【获取仓库列表】请求成功,返回数据:', response)
this.warehouseList = response.rows || []
this.loadingWarehouse = false
}).catch(error => {
console.error('获取仓库列表失败:', error)
console.error('【获取仓库列表】请求失败:', error)
this.warehouseList = []
this.loadingWarehouse = false
})
},
// 根据仓库ID获取仓库名称 - 适配后端的warehousesId
// 根据仓库ID获取仓库名称
getWarehouseNameById(warehouseId) {
console.log('【根据仓库ID获取名称】仓库ID:', warehouseId)
if (!warehouseId) return '-'
// 兼容匹配:同时匹配warehousesId/warehouseId/id
const warehouse = this.warehouseList.find(item =>
item.warehousesId === warehouseId || item.warehouseId === warehouseId || item.id === warehouseId
)
return warehouse ? (warehouse.warehousesName || warehouse.name || warehouse.warehouseName || '-') : '-'
const warehouseName = warehouse ? (warehouse.warehousesName || warehouse.name || warehouse.warehouseName || '-') : '-'
console.log('【根据仓库ID获取名称】匹配到的仓库名称:', warehouseName)
return warehouseName
},
// 初始化物料编码-名称映射表
async initMaterialCodeToNameMap() {
console.log('【初始化物料映射表】开始加载物料数据')
try {
this.materialMapLoaded = true
let pageNum = 1
......@@ -833,6 +823,7 @@ export default {
this.materialCodeToNameMap = {}
while (hasMore) {
console.log(`【初始化物料映射表】请求第${pageNum}页物料数据,每页${pageSize}条`)
const response = await listMaterials({
pageNum,
pageSize,
......@@ -842,6 +833,7 @@ export default {
})
if (response.rows && response.rows.length) {
console.log(`【初始化物料映射表】第${pageNum}页返回${response.rows.length}条物料数据`)
response.rows.forEach(item => {
if (item.materialCode && item.materialName) {
const code = item.materialCode.trim().toUpperCase()
......@@ -854,37 +846,40 @@ export default {
hasMore = false
}
}
console.log('物料映射表初始化完成:', this.materialCodeToNameMap)
console.log('【初始化物料映射表】完成,映射表长度:', Object.keys(this.materialCodeToNameMap).length)
} catch (error) {
console.error('初始化物料编码-名称映射表失败:', error)
console.error('【初始化物料映射表】失败:', error)
this.materialMapLoaded = false
this.$modal.msgError('物料数据加载失败,请刷新页面重试!')
}
},
// 行点击事件(展开/收起)
// 行点击事件
handleRowClick(row, event, column) {
console.log('【行点击事件】点击的行数据:', row, '列类型:', column.type)
if (column.type !== 'selection') {
this.$refs.locationsTable?.toggleRowExpansion(row)
}
},
// 获取库位列表 - 适配后端的warehousesId参数
// 获取库位列表
getList() {
console.log('【获取库位列表】查询参数:', this.queryParams)
this.loading = true
// 转换参数:将前端的warehouseId映射为后端需要的warehousesId
const params = {
...this.queryParams,
isUsed: 1,
warehousesId: this.queryParams.warehouseId, // 新增:传递warehousesId给后端
warehouseId: undefined // 移除:避免重复传参
warehousesId: this.queryParams.warehouseId,
warehouseId: undefined
}
console.log('【获取库位列表】最终请求参数:', params)
listLocations(params).then(response => {
console.log('【获取库位列表】请求成功,返回数据:', response)
this.locationsList = response.rows || []
this.total = response.total || 0
this.loading = false
}).catch(error => {
console.error('获取库位列表失败:', error)
console.error('【获取库位列表】请求失败:', error)
this.locationsList = []
this.total = 0
this.loading = false
......@@ -893,12 +888,14 @@ export default {
// 查询事件
handleQuery() {
console.log('【查询事件】执行查询操作')
this.queryParams.pageNum = 1
this.getList()
},
// 重置查询条件
resetQuery() {
console.log('【重置查询条件】执行重置操作')
this.queryParams = {
pageNum: 1,
pageSize: 10,
......@@ -907,7 +904,6 @@ export default {
warehouseId: null,
locationType: null,
layerCode: null,
// 重置新增的查询参数
locationUsage: null,
allowMixedProducts: null,
zoneCode: null,
......@@ -919,20 +915,24 @@ export default {
// 表格选择事件
handleSelectionChange(selection) {
console.log('【表格选择事件】选中的行:', selection)
this.ids = selection.map(item => item.id)
this.single = selection.length !== 1
this.multiple = !selection.length
console.log('【表格选择事件】处理后 - ids:', this.ids, 'single:', this.single, 'multiple:', this.multiple)
},
// 新增库位
handleAdd() {
console.log('【新增库位】执行新增操作')
this.reset()
this.open = true
this.title = "添加库位"
},
// 修改库位 - 适配后端返回的warehousesId字段
// 修改库位 - 核心优化:适配数组格式的物料/分类数据
async handleUpdate(row) {
console.log('【修改库位】触发修改操作,传入的行数据:', row)
this.reset()
const id = row?.id || this.ids?.[0]
if (!id) {
......@@ -941,14 +941,16 @@ export default {
}
try {
console.log('【修改库位】请求库位详情,ID:', id)
const response = await getLocations(id)
console.log('后端返回库位详情:', response.data)
console.log('【修改库位】后端返回库位详情:', response)
const rowData = response.data
// 映射后端的warehousesId到前端的warehouseId
// 映射后端字段到前端
const formData = {
...rowData,
warehouseId: rowData.warehousesId || rowData.warehouseId || rowData.id, // 优先级:warehousesId > warehouseId > id
warehousesId: undefined // 移除:前端只维护warehouseId
warehouseId: rowData.warehousesId || rowData.warehouseId || rowData.id,
warehousesId: undefined
}
this.form = JSON.parse(JSON.stringify(formData))
......@@ -959,154 +961,284 @@ export default {
await this.initMaterialCodeToNameMap()
// 从allowedCategoryIds读取物料编码(替代缺失的materialCodes)
const rawMaterialCodes = this.form.allowedCategoryIds || row.allowedCategoryIds || ''
console.log('原始物料编码(从allowedCategoryIds读取):', rawMaterialCodes)
// ========== 核心修改:处理数组格式的物料/分类数据 ==========
console.log('【修改库位】开始处理物料数据,原始数据:', {
materialIds: rowData.materialIds,
materialCodes: rowData.materialCodes,
names: rowData.names,
materialNames: rowData.materialNames,
categoryIds: rowData.categoryIds,
allowedCategoryIds: rowData.allowedCategoryIds
})
// 处理物料编码和名称(支持数组或字符串格式)
let materialCodes = []
let materialNames = []
if (rawMaterialCodes) {
const materialCodes = rawMaterialCodes.split(',')
// 优先从materialIds数组读取
if (Array.isArray(rowData.materialIds) && rowData.materialIds.length) {
materialCodes = rowData.materialIds
.filter(code => code && code.trim()) // 过滤null/空值
.map(code => code.trim().toUpperCase())
.filter(code => code)
console.log('处理后物料编码:', materialCodes)
console.log('映射表匹配:', materialCodes.map(code => ({
code,
name: this.materialCodeToNameMap[code]
})))
.filter((code, index, self) => self.indexOf(code) === index) // 去重
console.log('【修改库位】从materialIds数组读取物料编码:', materialCodes)
}
// 兼容字符串格式
else if (rowData.materialCodes) {
materialCodes = rowData.materialCodes.split(',')
.filter(code => code && code.trim())
.map(code => code.trim().toUpperCase())
.filter((code, index, self) => self.indexOf(code) === index)
console.log('【修改库位】从materialCodes字符串读取物料编码:', materialCodes)
}
this.form.materialNames = materialCodes.map(code => {
// 处理物料名称(支持数组或字符串格式)
if (Array.isArray(rowData.names) && rowData.names.length) {
materialNames = rowData.names
.filter(name => name && name.trim())
.filter((name, index, self) => self.indexOf(name) === index)
console.log('【修改库位】从names数组读取物料名称:', materialNames)
}
else if (rowData.materialNames) {
materialNames = rowData.materialNames.split(',')
.filter(name => name && name.trim())
.filter((name, index, self) => self.indexOf(name) === index)
console.log('【修改库位】从materialNames字符串读取物料名称:', materialNames)
}
// 兜底:通过物料编码映射名称
else if (materialCodes.length) {
materialNames = materialCodes.map(code => {
return this.materialCodeToNameMap[code] || `【未匹配】${code}`
}).join(',')
})
console.log('【修改库位】通过物料编码映射名称:', materialNames)
}
// 同步赋值materialCodes(确保选择器能接收到)
this.form.materialCodes = rawMaterialCodes
// 处理分类ID(支持数组或字符串格式)
let categoryIds = []
if (Array.isArray(rowData.categoryIds) && rowData.categoryIds.length) {
categoryIds = rowData.categoryIds
.filter(id => id && id.trim())
.filter((id, index, self) => self.indexOf(id) === index)
console.log('【修改库位】从categoryIds数组读取分类ID:', categoryIds)
}
else if (rowData.allowedCategoryIds) {
categoryIds = rowData.allowedCategoryIds.split(',')
.filter(id => id && id.trim())
.filter((id, index, self) => self.indexOf(id) === index)
console.log('【修改库位】从allowedCategoryIds字符串读取分类ID:', categoryIds)
}
// 更新表单数据
this.form.materialCodes = materialCodes.join(',')
this.form.materialNames = materialNames.join(',')
this.form.allowedCategoryIds = categoryIds.join(',')
// 处理分类名称
if (categoryIds.length && this.$refs.materialsSeletor) {
const categoryNames = categoryIds.map(code => {
const rawCode = code.replace(/-/g, '')
return this.$refs.materialsSeletor.categoryMap?.[rawCode] || code
})
this.form.allowedCategoryNames = categoryNames.join(',')
console.log('【修改库位】处理后的分类名称:', categoryNames)
} else if (rowData.allowedCategoryNames) {
this.form.allowedCategoryNames = rowData.allowedCategoryNames
console.log('【修改库位】使用原始分类名称:', rowData.allowedCategoryNames)
}
// 更新临时选择数据
this.tempSelectedMaterials = {
materialCodes: materialCodes,
names: this.form.materialNames.split(',').filter(name => name.trim()),
categoryIds: this.form.allowedCategoryIds ? this.form.allowedCategoryIds.split(',').filter(c => c.trim()) : []
}
names: materialNames,
categoryIds: categoryIds
}
console.log('【修改库位】处理后的物料数据:', {
materialCodes: this.form.materialCodes,
materialNames: this.form.materialNames,
categoryIds: this.form.allowedCategoryIds
})
this.open = true
this.title = "修改库位"
} catch (error) {
console.error('获取库位详情失败:', error)
console.error('【修改库位】获取库位详情失败:', error)
this.$modal.msgError('获取数据失败,请重试!')
}
},
// 提交表单 - 适配后端的warehousesId参数
// 提交表单
submitForm() {
console.log('【提交表单】开始验证表单,当前表单数据:', this.form)
this.$refs["form"].validate(valid => {
if (valid) {
// 处理物料编码和名称
if (this.form.materialCodes) {
this.form.materialCodes = this.form.materialCodes.split(',').filter(code => code.trim()).join(',')
}
if (this.form.materialNames) {
this.form.materialNames = this.form.materialNames.split(',').filter(name => name.trim()).join(',')
console.log('【提交表单】表单验证通过,开始格式化数据')
// 格式化物料相关字段(去重、过滤空值)
const formatField = (value) => {
if (!value) return ''
const result = value.split(',')
.filter(item => item && item.trim())
.filter((item, index, self) => self.indexOf(item) === index)
.join(',')
console.log(`【提交表单】格式化字段值 "${value}" 结果: "${result}"`)
return result
}
// 转换表单数据:将前端的warehouseId映射为后端需要的warehousesId
this.form.materialCodes = formatField(this.form.materialCodes)
this.form.materialNames = formatField(this.form.materialNames)
this.form.allowedCategoryIds = formatField(this.form.allowedCategoryIds)
this.form.allowedCategoryNames = formatField(this.form.allowedCategoryNames)
// 转换表单数据
const submitData = {
...this.form,
warehousesId: this.form.warehouseId, // 新增:传递warehousesId给后端
warehouseId: undefined, // 移除:避免重复传参
warehouseName: undefined // 移除:只传ID给后端
warehousesId: this.form.warehouseId,
warehouseId: undefined,
warehouseName: undefined
}
console.log('【提交表单】最终提交数据:', submitData)
const request = this.form.id != null ? updateLocations(submitData) : addLocations(submitData)
request.then(response => {
console.log('【提交表单】请求成功:', response)
this.$modal.msgSuccess(this.form.id != null ? "修改成功" : "新增成功")
this.open = false
this.getList()
}).catch(error => {
console.error('提交库位数据失败:', error)
console.error('【提交表单】请求失败:', error)
this.$modal.msgError(this.form.id != null ? "修改失败" : "新增失败")
})
} else {
console.log('【提交表单】表单验证失败')
}
})
},
// 删除库位
handleDelete(row) {
console.log('【删除库位】触发删除操作,行数据:', row)
const ids = row?.id ? [row.id] : this.ids
if (!ids || ids.length === 0) {
this.$modal.msgWarning('请选择要删除的库位!')
return
}
console.log('【删除库位】待删除ID:', ids)
this.$modal.confirm(
row?.id ? `是否确认删除库位编号为"${row.locationCode}"的数据项?` : `是否确认删除选中的${ids.length}条库位数据项?`
).then(() => {
console.log('【删除库位】用户确认删除,执行删除请求')
return delLocations(ids)
}).then(() => {
console.log('【删除库位】删除成功')
this.getList()
this.$modal.msgSuccess("删除成功")
}).catch(() => {})
}).catch(() => {
console.log('【删除库位】用户取消删除或请求失败')
})
},
// 导出库位 - 适配后端的warehousesId参数
// 导出库位
handleExport() {
// 转换导出参数:将前端的warehouseId映射为后端需要的warehousesId
console.log('【导出库位】执行导出操作,查询参数:', this.queryParams)
const exportParams = {
...this.queryParams,
warehousesId: this.queryParams.warehouseId,
warehouseId: undefined
}
console.log('【导出库位】最终导出参数:', exportParams)
this.download('inventory/locations/export', exportParams, `locations_${new Date().getTime()}.xlsx`)
},
// 物料选择变化
// 物料选择变化 - 优化数据处理
handleMaterialSelectionChange(selectedData) {
console.log('【物料选择变化】接收到的选择数据:', selectedData)
// 修复:从selectedData中取materialIds(而非materialCodes)
const materialCodes = (selectedData.materialIds || [])
.filter(code => code && code.trim())
.map(code => code.trim().toUpperCase())
.filter((code, index, self) => self.indexOf(code) === index)
// 物料名称取selectedData.names
const names = (selectedData.names || [])
.filter(name => name && name.trim())
.filter((name, index, self) => self.indexOf(name) === index)
// 分类ID取selectedData.categoryIds
const categoryIds = (selectedData.categoryIds || [])
.filter(id => id && id.trim())
.filter((id, index, self) => self.indexOf(id) === index)
this.tempSelectedMaterials = {
materialCodes: selectedData.materialCodes || [],
names: selectedData.names || [],
categoryIds: selectedData.formattedCategoryIds || []
materialCodes, // 保持变量名不变(后续逻辑依赖)
names,
categoryIds
}
console.log('【物料选择变化】处理后的临时选择数据:', this.tempSelectedMaterials)
},
// 取消物料选择
handleMaterialSelectionCancel() {
console.log('【取消物料选择】执行取消操作,恢复原有选择状态')
this.showMaterialSelect = false
// 恢复原有选择状态
this.tempSelectedMaterials = {
materialCodes: this.form.materialCodes ? this.form.materialCodes.split(',').filter(u => u.trim()) : [],
names: this.form.materialNames ? this.form.materialNames.split(',').filter(n => n.trim()) : [],
categoryIds: this.form.allowedCategoryIds ? this.form.allowedCategoryIds.split(',').filter(c => c.trim()) : []
}
console.log('【取消物料选择】恢复后的临时选择数据:', this.tempSelectedMaterials)
},
// 确认物料选择
// 确认物料选择 - 优化数据格式化
confirmMaterialSelection() {
console.log('【确认物料选择】当前临时选择数据:', this.tempSelectedMaterials)
// 修复:判断tempSelectedMaterials.materialCodes是否有值
if (!this.tempSelectedMaterials.materialCodes.length) {
console.warn('【确认物料选择】未选择任何物料,提示用户')
this.$modal.msgWarning('请至少选择一个物料!')
return
}
// 更新表单数据(确保去重和过滤)
this.form.materialCodes = this.tempSelectedMaterials.materialCodes.join(',')
this.form.materialNames = this.tempSelectedMaterials.names.join(',')
this.form.allowedCategoryIds = this.tempSelectedMaterials.categoryIds.join(',')
if (this.$refs.materialsSeletor) {
// 生成分类名称
if (this.$refs.materialsSeletor && this.tempSelectedMaterials.categoryIds.length) {
const categoryNames = this.tempSelectedMaterials.categoryIds.map(code => {
const rawCode = code.replace(/-/g, '')
return this.$refs.materialsSeletor.categoryMap[rawCode] || code
})
return this.$refs.materialsSeletor.categoryMap?.[rawCode] || code
}).filter(name => name && name.trim())
this.form.allowedCategoryNames = categoryNames.join(',')
console.log('【确认物料选择】生成的分类名称:', categoryNames)
}
console.log('【确认物料选择】更新后的表单物料数据:', {
materialCodes: this.form.materialCodes,
materialNames: this.form.materialNames,
allowedCategoryIds: this.form.allowedCategoryIds,
allowedCategoryNames: this.form.allowedCategoryNames
})
this.showMaterialSelect = false
this.$modal.msgSuccess(`成功选择 ${this.tempSelectedMaterials.names.length} 个物料`)
},
// 导入库位 - 集成货主页面的导入逻辑
// 导入库位
handleImport() {
console.log('【导入库位】打开导入组件')
this.$refs.import.show()
},
// 重置表单
reset() {
console.log('【重置表单】执行表单重置')
this.form = {
id: null,
locationCode: null,
......@@ -1129,7 +1261,6 @@ export default {
isEnabled: 1,
isUsed: 1,
sortNo: 0,
// 重置新增的表单字段
putawayOrder: null,
pickingOrder: null,
locationUsage: null,
......@@ -1147,10 +1278,12 @@ export default {
if (this.$refs.form) {
this.$refs.form.resetFields()
}
console.log('【重置表单】重置完成,表单数据:', this.form)
},
// 取消操作
cancel() {
console.log('【取消操作】关闭弹窗并重置表单')
this.open = false
this.reset()
}
......
......@@ -83,7 +83,6 @@
<el-table-column prop="outboundOrderId" label="出库单号" width="150" />
<el-table-column prop="warehousesName" label="仓库名称" width="120" />
<el-table-column prop="locationName" label="库位名称" width="120" />
<el-table-column prop="locationId" label="库位ID" width="140" />
<el-table-column
prop="inventoryType"
label="库存类型"
......@@ -146,7 +145,7 @@
v-for="color in dict.type.label_color"
:key="color.value"
:label="color.label"
:value="color.value"
:value="Number(color.value)"
/>
</el-select>
</el-form-item>
......@@ -230,7 +229,6 @@
<el-table-column prop="batchId" label="批次ID" width="150" />
<el-table-column prop="warehousesName" label="仓库名称" width="120" />
<el-table-column prop="locationName" label="库位名称" width="120" />
<el-table-column prop="locationId" label="库位ID" width="140" />
<el-table-column prop="plannedQuantity" label="计划数量" width="100" />
<el-table-column prop="actualQuantity" label="实际数量" width="100" />
<el-table-column prop="unitPrice" label="单价" width="100" />
......
......@@ -74,10 +74,10 @@
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="库类型" prop="orderTypeId">
<el-form-item label="库类型" prop="orderTypeId">
<el-input
v-model="queryParams.orderTypeId"
placeholder="请输入库类型"
placeholder="请输入库类型"
clearable
@keyup.enter.native="handleQuery"
/>
......@@ -192,7 +192,7 @@
<el-table-column type="selection" width="55" align="center" fixed />
<el-table-column label="出库单号" align="center" prop="orderId" width="150" fixed/>
<el-table-column label="系统编号" align="center" prop="systemNo" width="150" />
<el-table-column label="库类型" align="center" prop="orderTypeId" width="120">
<el-table-column label="库类型" align="center" prop="orderTypeId" width="120">
<template slot-scope="scope">
<dict-tag v-if="dict.type.outbound_order_type" :options="dict.type.outbound_order_type" :value="scope.row.orderTypeId"/>
<span v-else>-</span>
......@@ -201,7 +201,7 @@
<!-- 新增:订单类型列 -->
<el-table-column label="订单类型" align="center" prop="orderType" width="120">
<template slot-scope="scope">
<dict-tag v-if="dict.type.outbound_order_type" :options="dict.type.outbound_order_type" :value="scope.row.orderType"/>
<dict-tag v-if="dict.type.order_type" :options="dict.type.order_type" :value="scope.row.orderType"/>
<span v-else>-</span>
</template>
</el-table-column>
......@@ -299,7 +299,7 @@
<el-form-item label="入库类型" prop="orderTypeId">
<el-select v-model="form.orderTypeId" placeholder="请选择入库类型" :disabled="isViewDetail || formDisabled.orderTypeId" style="width: 100%">
<el-option
v-for="item in dict.type.inbound_order_type"
v-for="item in dict.type.outbound_order_type"
:key="item.value"
:label="item.label"
:value="item.value"
......@@ -314,7 +314,7 @@
<el-form-item label="订单类型" prop="orderType">
<el-select v-model="form.orderType" placeholder="请选择订单类型" :disabled="isViewDetail || formDisabled.orderType" style="width: 100%">
<el-option
v-for="item in dict.type.outbound_order_type"
v-for="item in dict.type.order_type"
:key="item.value"
:label="item.label"
:value="item.value"
......@@ -518,9 +518,12 @@
ref="import"
title="导入"
import-url="/inventory/orders/import"
template-url="inventory/orders/importTemplate"
template-name="owners_importTemplate"
template-url="/inventory/orders/importTemplate"
template-name="入库单导入模板"
@success="getList"
:show-trdc-checkbox="true"
@orderTypeChange="handleOrderTypeChange"
:orderTypeRequired="true"
/>
</div>
</template>
......@@ -534,10 +537,11 @@ import WarehouseSelector from "@/views/compononents/WarehouseSelector.vue"
import LocationSelector from "@/views/compononents/LocationSelector.vue"
import OwnerSelector from "@/views/compononents/OwnerSelector.vue"
import PageTitle from "@/components/PageTitle"
import ImportExcel from "@/components/ImportExcel/index"
export default {
name: "Orders",
dicts: ['outbound_order_type', 'inbound_order_type', 'inbound_order_status', 'label_color'],
dicts: ['outbound_order_type', 'inbound_order_type', 'inbound_order_status', 'label_color','order_type'],
components: {
OutboundOrderFormWithItems,
WarehouseSelector,
......@@ -677,6 +681,9 @@ export default {
},
methods: {
handleOrderTypeChange(selection) {
this.form.orderTypeId = selection
},
/** 打开导入弹窗 */
handleImport() {
this.$refs.import.show()
......@@ -934,8 +941,7 @@ handleOwnerSelected(owner) {
this.detailDialogOpen = true
})
},
// 修复:接收子组件提交的明细数据
handleDetailSubmit(details) {
handleDetailSubmit(details) {
// 确保是数组格式
const detailList = Array.isArray(details) ? details : [details];
if (detailList.length === 0) {
......@@ -983,7 +989,8 @@ handleOwnerSelected(owner) {
sortNo: 0,
remark: item.remark || '',
warehouseName: item.warehouseName || this.form.warehouseName || '',
locationName: item.locationName || ''
locationName: item.locationName || '',
batchCode: item.batchId || '' // 新增:将子组件的batchId赋值给batchCode
};
});
......@@ -1019,7 +1026,8 @@ handleOwnerSelected(owner) {
sortNo: 0,
remark: item.remark || '',
warehouseName: item.warehouseName || this.form.warehouseName || '',
locationName: item.locationName || ''
locationName: item.locationName || '',
batchCode: item.batchId || '' // 新增:将子组件的batchId赋值给batchCode
};
});
......@@ -1042,7 +1050,7 @@ handleOwnerSelected(owner) {
this.detailDialogOpen = false;
// 强制更新视图
this.$forceUpdate();
},
},
// 计算主表总数量和总金额
calcTotalQuantity() {
let totalPlanned = 0
......
......@@ -158,12 +158,12 @@ public class OutboundOrdersController extends BaseController
@PreAuthorize("@ss.hasPermi('inventory:inbound:import')")
@Log(title = "入库信息导入", businessType = BusinessType.IMPORT)
@PostMapping("/import")
public AjaxResult importTemplate(MultipartFile file , boolean updateSupport) throws Exception
public AjaxResult importTemplate(MultipartFile file , boolean updateSupport,Integer orderType) throws Exception
{
ExcelUtil<OutboundTemplateVO> util = new ExcelUtil<OutboundTemplateVO>(OutboundTemplateVO.class);
List<OutboundTemplateVO> inboundOrders = util.importExcel(file.getInputStream());
String operName = getUsername();
String message = outboundOrdersService.importOutboundOrders(inboundOrders, updateSupport, operName);
String message = outboundOrdersService.importOutboundOrders(inboundOrders, updateSupport, operName,orderType);
return success(message);
}
}
......@@ -125,7 +125,13 @@ public class Inventory extends BaseEntity
/** 预警类型 */
private String alertType;
public String getWarehousesId() {
return warehousesId;
}
public void setWarehousesId(String warehousesId) {
this.warehousesId = warehousesId;
}
/** 最低库存 */
private Long minStockLevel;
......
......@@ -68,7 +68,6 @@ public class InboundOutboundStatisticsVO extends BaseEntity {
/** 库位ID */
private String locationId;
/** 物料编码/ID查询 */
private String materialCode;
......
......@@ -3,6 +3,7 @@ package com.ruoyi.inventory.domain.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;
import lombok.Data;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
......@@ -15,6 +16,7 @@ import java.util.Date;
* @author ruoyi
* @date 2025-12-10
*/
@Data
public class OutboundOrdersSummaryVO extends BaseEntity
{
private static final long serialVersionUID = 1L;
......@@ -56,6 +58,9 @@ public class OutboundOrdersSummaryVO extends BaseEntity
@Excel(name = "包装重量")
private BigDecimal packageWeight;
private String itemStatus;
private String unitPrice;
/** 总重量 */
@Excel(name = "总重量")
private BigDecimal totalWeight;
......@@ -124,265 +129,10 @@ public class OutboundOrdersSummaryVO extends BaseEntity
@JsonFormat(pattern = "yyyy-MM-dd")
private Date endDate;
/** 订单状态(检索条件) */
@Excel(name = "订单状态 字典,检索条件")
private Long orderStatus;
/** 库位ID(检索条件) */
@Excel(name = "库位ID 检索条件")
private String locationId;
// ========== Getter & Setter 方法 ==========
public String getMaterialId() {
return materialId;
}
public void setMaterialId(String materialId) {
this.materialId = materialId;
}
public String getMaterialName() {
return materialName;
}
public void setMaterialName(String materialName) {
this.materialName = materialName;
}
public String getSapNo() {
return sapNo;
}
public void setSapNo(String sapNo) {
this.sapNo = sapNo;
}
public String getTsCode() {
return tsCode;
}
public void setTsCode(String tsCode) {
this.tsCode = tsCode;
}
public String getHazardId() {
return hazardId;
}
public void setHazardId(String hazardId) {
this.hazardId = hazardId;
}
public String getSpecification() {
return specification;
}
public void setSpecification(String specification) {
this.specification = specification;
}
public String getMaterialUnit() {
return materialUnit;
}
public void setMaterialUnit(String materialUnit) {
this.materialUnit = materialUnit;
}
public BigDecimal getUnitWeight() {
return unitWeight;
}
public void setUnitWeight(BigDecimal unitWeight) {
this.unitWeight = unitWeight;
}
public BigDecimal getPackageWeight() {
return packageWeight;
}
public void setPackageWeight(BigDecimal packageWeight) {
this.packageWeight = packageWeight;
}
public BigDecimal getTotalWeight() {
return totalWeight;
}
public void setTotalWeight(BigDecimal totalWeight) {
this.totalWeight = totalWeight;
}
public BigDecimal getVolume() {
return volume;
}
public void setVolume(BigDecimal volume) {
this.volume = volume;
}
public Long getShelfLifeDays() {
return shelfLifeDays;
}
public void setShelfLifeDays(Long shelfLifeDays) {
this.shelfLifeDays = shelfLifeDays;
}
public String getStorageTemperature() {
return storageTemperature;
}
public void setStorageTemperature(String storageTemperature) {
this.storageTemperature = storageTemperature;
}
public String getSpecialRequirements() {
return specialRequirements;
}
public void setSpecialRequirements(String specialRequirements) {
this.specialRequirements = specialRequirements;
}
public Long getSortNo() {
return sortNo;
}
public void setSortNo(Long sortNo) {
this.sortNo = sortNo;
}
private String locationName;
public BigDecimal getPlannedQuantity() {
return plannedQuantity;
}
public void setPlannedQuantity(BigDecimal plannedQuantity) {
this.plannedQuantity = plannedQuantity;
}
public BigDecimal getActualQuantity() {
return actualQuantity;
}
public void setActualQuantity(BigDecimal actualQuantity) {
this.actualQuantity = actualQuantity;
}
public BigDecimal getTotalAmount() {
return totalAmount;
}
public void setTotalAmount(BigDecimal totalAmount) {
this.totalAmount = totalAmount;
}
public String getWarehouseId() {
return warehouseId;
}
public void setWarehouseId(String warehouseId) {
this.warehouseId = warehouseId;
}
public String getWarehouseName() {
return warehouseName;
}
public void setWarehouseName(String warehouseName) {
this.warehouseName = warehouseName;
}
public String getBatchCode() {
return batchCode;
}
public void setBatchCode(String batchCode) {
this.batchCode = batchCode;
}
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
public String getMainOrderId() {
return mainOrderId;
}
public void setMainOrderId(String mainOrderId) {
this.mainOrderId = mainOrderId;
}
public Date getStartDate() {
return startDate;
}
public void setStartDate(Date startDate) {
this.startDate = startDate;
}
public Date getEndDate() {
return endDate;
}
public void setEndDate(Date endDate) {
this.endDate = endDate;
}
public Long getOrderStatus() {
return orderStatus;
}
public void setOrderStatus(Long orderStatus) {
this.orderStatus = orderStatus;
}
public String getLocationId() {
return locationId;
}
public void setLocationId(String locationId) {
this.locationId = locationId;
}
// ========== ToString 方法 ==========
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("materialId", getMaterialId())
.append("materialName", getMaterialName())
.append("sapNo", getSapNo())
.append("tsCode", getTsCode())
.append("hazardId", getHazardId())
.append("specification", getSpecification())
.append("materialUnit", getMaterialUnit())
.append("unitWeight", getUnitWeight())
.append("packageWeight", getPackageWeight())
.append("totalWeight", getTotalWeight())
.append("volume", getVolume())
.append("shelfLifeDays", getShelfLifeDays())
.append("storageTemperature", getStorageTemperature())
.append("specialRequirements", getSpecialRequirements())
.append("sortNo", getSortNo())
.append("plannedQuantity", getPlannedQuantity())
.append("actualQuantity", getActualQuantity())
.append("totalAmount", getTotalAmount())
.append("warehouseId", getWarehouseId())
.append("warehouseName", getWarehouseName())
.append("batchCode", getBatchCode())
.append("orderId", getOrderId())
.append("mainOrderId", getMainOrderId())
.append("startDate", getStartDate())
.append("endDate", getEndDate())
.append("orderStatus", getOrderStatus())
.append("locationId", getLocationId())
.append("remark", getRemark())
.append("createTime", getCreateTime())
.append("updateTime", getUpdateTime())
.toString();
}
}
\ No newline at end of file
......@@ -46,6 +46,6 @@ public class StorageLocationsLocationTemplateVO {
@Excel(name = "允许混放产品",dictType="yorn")
private String allowMixedProducts;
@Excel(name = "允许混放批次")
@Excel(name = "允许混放批次",dictType="yorn")
private String allowMixedBatches;
}
\ No newline at end of file
......@@ -78,5 +78,5 @@ public interface IOutboundOrdersService
public String outboundOrdersCount();
@Transactional(rollbackFor = Exception.class)
String importOutboundOrders(List<OutboundTemplateVO> inboundOrdersList, Boolean isUpdateSupport, String operName);
String importOutboundOrders(List<OutboundTemplateVO> inboundOrdersList, Boolean isUpdateSupport, String operName,Integer orderType);
}
......@@ -252,7 +252,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
@Transactional(rollbackFor = Exception.class)
@Override
public String importOutboundOrders(List<OutboundTemplateVO> inboundOrdersList, Boolean isUpdateSupport, String operName) {
public String importOutboundOrders(List<OutboundTemplateVO> inboundOrdersList, Boolean isUpdateSupport, String operName,Integer orderType) {
// 1. 基础空值校验(完全保留你的代码)
if (CollectionUtils.isEmpty(inboundOrdersList)) {
throw new ServiceException("导入数据不能为空!");
......@@ -348,6 +348,7 @@ public class OutboundOrdersServiceImpl implements IOutboundOrdersService
mainDO.setOrderStatus(3L);
mainDO.setCreateBy(operId);
mainDO.setCreateTime(now);
mainDO.setOrderType(Long.valueOf(orderType));
mainDO.setCreateUserCode(operId);
mainDO.setUpdateBy(operId);
mainDO.setUpdateTime(now);
......
......@@ -270,6 +270,7 @@ public class StorageLocationsServiceImpl implements IStorageLocationsService
storageLocations.setCreateBy(operId);
storageLocations.setCreateTime(now);
storageLocations.setCreateUserCode(operId);
storageLocations.setWarehousesId("572ba484-199c-45d9-9735-610928ed5c70");
// 设置默认值
if (storageLocations.getIsUsed() == null) {
......
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.inventory.mapper.InventoryMapper">
<resultMap type="Inventory" id="InventoryResult">
......@@ -44,7 +44,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="unitWeight" column="unit_weight" />
<result property="packageWeight" column="package_weight" />
<result property="totalWeight" column="total_weight" />
<result property="volume" column="volume" />
<result property="shelfLifeDays" column="shelf_life_days" />
<result property="storageTemperature" column="storage_temperature" />
<result property="specialRequirements" column="special_requirements" />
......@@ -89,6 +88,52 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="unitPrice" column="unit_price" />
</resultMap>
<resultMap type="com.ruoyi.inventory.domain.vo.InventoryVo" id="InventoryVoResult">
<result property="id" column="id" />
<result property="inventoryType" column="inventory_type" />
<result property="orderId" column="order_id" />
<result property="materialId" column="material_id" />
<result property="batchId" column="batch_id" />
<result property="warehousesCode" column="warehouses_code" />
<result property="locationId" column="location_id" />
<result property="ownerId" column="owner_id" />
<result property="quantity" column="quantity" />
<result property="lockedQuantity" column="locked_quantity" />
<result property="unitWeight" column="unit_weight" />
<result property="totalWeight" column="total_weight" />
<result property="totalVolume" column="total_volume" />
<result property="productionDate" column="production_date" />
<result property="expirationDate" column="expiration_date" />
<result property="inventoryStatus" column="inventory_status" />
<result property="lastInboundTime" column="last_inbound_time" />
<result property="lastOutboundTime" column="last_outbound_time" />
<result property="isUsed" column="is_used" />
<result property="sortNo" column="sort_no" />
<result property="createTime" column="create_time" />
<result property="createUserCode" column="create_user_code" />
<result property="updateTime" column="update_time" />
<result property="updateUserCode" column="update_user_code" />
<result property="warehousesId" column="warehouses_id" />
<result property="materialName" column="material_name" />
<result property="minStockLevel" column="min_stock_level" jdbcType="BIGINT"/>
<result property="maxStockLevel" column="max_stock_level" jdbcType="BIGINT"/>
<result property="sapNo" column="sap_no" />
<result property="tsCode" column="ts_code" />
<result property="hazardId" column="hazard_id" />
<result property="specification" column="specification" />
<result property="materialUnit" column="material_unit" />
<result property="unitWeight" column="unit_weight" />
<result property="packageWeight" column="package_weight" />
<result property="totalWeight" column="total_weight" />
<result property="shelfLifeDays" column="shelf_life_days" />
<result property="storageTemperature" column="storage_temperature" />
<result property="specialRequirements" column="special_requirements" />
<result property="alterType" column="alterType" />
<result property="warehousesName" column="warehouses_name" />
<result property="locationName" column="location_name" />
<result property="ownerName" column="owner_name" />
</resultMap>
<resultMap type="com.ruoyi.inventory.domain.vo.InventorySummaryVO" id="InventorySummaryResult">
<result property="materialId" column="material_id" />
<result property="materialName" column="material_name" />
......@@ -102,7 +147,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="unitWeight" column="unit_weight" />
<result property="packageWeight" column="package_weight" />
<result property="totalWeight" column="total_weight" />
<result property="volume" column="volume" />
<result property="shelfLifeDays" column="shelf_life_days" />
<result property="storageTemperature" column="storage_temperature" />
<result property="specialRequirements" column="special_requirements" />
......@@ -157,7 +201,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
m.unit_weight,
m.package_weight,
m.total_weight,
m.volume,
m.shelf_life_days,
m.storage_temperature,
m.special_requirements,
......@@ -193,7 +236,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
m.unit_weight,
m.package_weight,
m.total_weight,
m.volume,
m.shelf_life_days,
m.storage_temperature,
m.special_requirements ) tab
......@@ -218,7 +260,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="inventoryStatus != null "> and i.inventory_status = #{inventoryStatus}</if>
<if test="isUsed != null "> and i.is_used = #{isUsed}</if>
</where>
i.material_id, m.material_name, m.sap_no,
group by i.material_id, m.material_name, m.sap_no,
m.ts_code,
m.hazard_id,
m.specification,
......@@ -226,7 +268,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
m.unit_weight,
m.package_weight,
m.total_weight,
m.volume,
m.shelf_life_days,
m.storage_temperature,
m.special_requirements) tab
......@@ -268,7 +309,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
m.unit_weight,
m.package_weight,
m.total_weight,
m.volume,
m.shelf_life_days,
m.storage_temperature,
m.special_requirements,
......@@ -535,7 +575,7 @@ and inventory_status = '1'
#{id}
</foreach>
</delete>
<!-- 库存物料超出预警值统计-->
<!-- 库存物料超出预警值统计-->
<resultMap id="InventoryExceedWarnResultMap" type="com.ruoyi.inventory.domain.vo.InventoryExceedWarnVO">
<result column="material_name" property="materialName" jdbcType="VARCHAR"/>
<result column="category_name" property="categoryName" jdbcType="VARCHAR"/>
......
......@@ -42,6 +42,12 @@
<result property="materialName" column="material_name" />
<result property="sapNo" column="sap_no" />
<result property="tsCode" column="ts_code" />
<result property="locationId" column="location_id" />
<result property="locationName" column="location_name" />
<result property="itemStatus" column="item_status" />
<result property="unitPrice" column="unit_price" />
<result property="hazardId" column="hazard_id" />
<result property="specification" column="specification" />
<result property="materialUnit" column="material_unit" />
......@@ -244,6 +250,8 @@
m.total_weight,
m.volume,
m.shelf_life_days,
oi.location_id,
sl.location_name as location_name,
m.storage_temperature,
m.special_requirements,
m.sort_no,
......@@ -254,7 +262,10 @@
w.warehouses_name as warehouse_name,
oi.batch_code,
oi.order_id,
o.order_id as main_order_id
oi.remark,
oi.unit_price,
o.order_id as main_order_id,
oi.item_status
from outbound_order_items oi
left join outbound_orders o on oi.outbound_order_id = o.id
left join materials m on oi.material_id = m.id
......@@ -269,7 +280,7 @@
<if test="endDate != null and endDate != ''"> and date_format(COALESCE(oi.shipped_at, o.inbound_date),'%Y-%m-%d') &lt;= #{endDate}</if>
<if test="warehouseId != null and warehouseId != ''"> and oi.warehouse_id = #{warehouseId}</if>
<if test="locationId != null and locationId != ''"> and oi.location_id = #{locationId}</if>
<if test="orderStatus != null "> and o.order_status = #{orderStatus}</if>
<if test="itemStatus != null "> and oi.item_status = #{itemStatus}</if>
</where>
group by oi.material_id, m.material_name, m.sap_no,
m.ts_code,
......
......@@ -108,6 +108,8 @@
<if test="pickingArea != null and pickingArea != ''"> and sl.picking_area = #{pickingArea}</if>
<if test="allowMixedProducts != null "> and sl.allow_mixed_products = #{allowMixedProducts}</if>
<if test="allowMixedBatches != null "> and sl.allow_mixed_batches = #{allowMixedBatches}</if>
<!-- 按sort_no降序排序 -->
order by sl.sort_no desc
</select>
<!-- 关联仓库表的列表查询(简化) -->
......@@ -129,6 +131,8 @@
<if test="pickingArea != null and pickingArea != ''"> and sl.picking_area = #{pickingArea}</if>
<if test="allowMixedProducts != null "> and sl.allow_mixed_products = #{allowMixedProducts}</if>
<if test="allowMixedBatches != null "> and sl.allow_mixed_batches = #{allowMixedBatches}</if>
<!-- 按sort_no降序排序 -->
order by sl.sort_no desc
</select>
<!-- 根据ID查询(调整:保留where,单独条件) -->
......@@ -180,12 +184,16 @@
<select id="selectStorageLocationsByWarehousesCode" parameterType="String" resultMap="StorageLocationsResult">
<include refid="selectStorageLocationsVo"/>
and sl.warehouses_code = #{warehousesCode}
<!-- 按sort_no降序排序 -->
order by sl.sort_no desc
</select>
<!-- 关联仓库表的仓库编码查询(简化) -->
<select id="selectStorageLocationsByWarehousesCodeWithWarehouses" parameterType="String" resultMap="StorageLocationsWithWarehousesResult">
<include refid="selectStorageLocationsWithWarehousesVo"/>
and sl.warehouses_code = #{warehousesCode}
<!-- 按sort_no降序排序 -->
order by sl.sort_no desc
</select>
<!-- 根据仓库编码列表查询(简化) -->
......@@ -195,6 +203,8 @@
<foreach item="id" collection="list" open="(" separator="," close=")">
#{id}
</foreach>
<!-- 按sort_no降序排序 -->
order by sl.sort_no desc
</select>
<!-- 关联仓库表的编码列表查询(简化) -->
......@@ -204,12 +214,16 @@
<foreach item="id" collection="list" open="(" separator="," close=")">
#{id}
</foreach>
<!-- 按sort_no降序排序 -->
order by sl.sort_no desc
</select>
<!-- 获取库位表 的 location_code 编码 location_name 做成字典-->
<select id="getMapList" parameterType="StorageLocations" resultType="java.util.Map">
select id, IFNULL(location_name, '') as location_name from storage_locations where is_used = 1
<if test="warehousesCode != null and warehousesCode != ''"> and warehouses_id = #{warehousesCode}</if>
<!-- 按sort_no降序排序 -->
order by sort_no desc
</select>
<!-- 关联仓库的字典查询 -->
......@@ -219,6 +233,8 @@
left join warehouses w on sl.warehouses_code = w.warehouses_code
where sl.is_used = 1
<if test="warehousesCode != null and warehousesCode != ''"> and sl.warehouses_code = #{warehousesCode}</if>
<!-- 按sort_no降序排序 -->
order by sl.sort_no desc
</select>
<!-- 原有新增/更新/删除方法(仅添加新字段) -->
......@@ -393,6 +409,8 @@
<if test="pickingArea != null and pickingArea != ''"> and sl.picking_area = #{pickingArea}</if>
<if test="allowMixedProducts != null "> and sl.allow_mixed_products = #{allowMixedProducts}</if>
<if test="allowMixedBatches != null "> and sl.allow_mixed_batches = #{allowMixedBatches}</if>
<!-- 按sort_no降序排序 -->
order by sl.sort_no desc
</select>
<!-- 关联仓库的getStorageLocationsList -->
......@@ -414,6 +432,8 @@
<if test="pickingArea != null and pickingArea != ''"> and sl.picking_area = #{pickingArea}</if>
<if test="allowMixedProducts != null "> and sl.allow_mixed_products = #{allowMixedProducts}</if>
<if test="allowMixedBatches != null "> and sl.allow_mixed_batches = #{allowMixedBatches}</if>
<!-- 按sort_no降序排序 -->
order by sl.sort_no desc
</select>
</mapper>
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论