更新装配、精工车间、技术中心、仓储模块,新增工序工价、生产审核、三楼仓库可视化等功能及文档SQL

This commit is contained in:
Developer
2026-07-06 11:21:59 +08:00
parent be0ff91e24
commit 9c1f52078d
43 changed files with 8566 additions and 233 deletions

View File

@@ -3,7 +3,7 @@
<el-card style="height: 850px">
<div style="margin-top: 7px; margin-bottom: 7px">
<!--{{ $t('m.music') }}-->
<el-input v-model="materialCode" style="width: 150px " size="small" clearable placeholder="物料编号"/>
<el-input v-model="materialCode" style="width: 150px " size="small" clearable placeholder="物料编码"/><!-- 备注:修改人:Ld 修改时间:2026-06-29 14:24:00 -->
<el-input v-model="materialName" style="width: 150px " size="small" clearable placeholder="物料名称"/>
<el-input v-model="specificationsModel" style="width: 150px " size="small" clearable placeholder="图号或型号"/>
<el-input v-model="LocationName" style="width: 100px " size="small" clearable placeholder="库位"/>

View File

@@ -5,7 +5,7 @@
<el-input
v-model="materialCodeValue"
style="width:200px"
placeholder="物料编号"
placeholder="物料编码"
size="small"
clearable
@keyup.enter.native="pageCurrent=1;searchTable()"/>
@@ -93,7 +93,7 @@
style="width:962px"
height="258px"
size="small">
<el-table-column label="物料编号" align="center" prop="物料码">
<el-table-column label="物料编码" align="center" prop="物料码"><!-- 备注:修改人:Ld 修改时间:2026-06-29 14:24:00 -->
<template slot-scope="scope">
{{ scope.row.物料编码 }}
</template>
@@ -143,7 +143,7 @@
</el-table-column>
<el-table-column
width="140px"
label="物料编号"
label="物料编码"
align="center"
show-overflow-tooltip
sortable="物料编码"
@@ -476,7 +476,7 @@ export default {
})
},
handleEdit(row, column) {
if (column.label === '物料编号') {
if (column.label === '物料编码') { // 备注:修改人:Ld 修改时间:2026-06-29 14:24:00
this.materialSerialNumber = row.物料流水号
this.tableData9 = []
this.searchTable2()
@@ -559,7 +559,7 @@ export default {
Promise.all(promises).then(function(results) {
var data = []
// 表头
data.push(['物料编号', '物料名称', '图号型号', '库位', '库存', '需求数', '可用库存', '加工中', '采购中', '毛坯数', '单位', '类型', '库存批次', '待入库', '最低库存', '安全库存', '最高库存', '', '物料编号', '物料名称', '图号/型号', '类型', '出/入库数', '出/入库日期', '批次编码'])
data.push(['物料编码', '物料名称', '图号型号', '库位', '库存', '需求数', '可用库存', '加工中', '采购中', '毛坯数', '单位', '类型', '库存批次', '待入库', '最低库存', '安全库存', '最高库存', '', '物料编码', '物料名称', '图号/型号', '类型', '出/入库数', '出/入库日期', '批次编码']) // 备注:修改人:Ld 修改时间:2026-06-29 14:24:00
results.forEach(function(item) {
var row = item.row
var mainRow = [

View File

@@ -2,7 +2,8 @@
<div class="app-container">
<el-card style="height: 850px">
<div style="margin-top: 7px">
<el-input v-model="production" style="width: 200px ; margin-left: 10px" size="small" clearable placeholder="物料名称/编号/规格/型号" @keyup.enter.native="pageCurrent=1;pageSize=20;searchTable()"/>
<el-input v-model="materialCodeValue" style="width: 160px ; margin-left: 10px" size="small" clearable placeholder="物料编码" @keyup.enter.native="pageCurrent=1;pageSize=20;searchTable()"/><!-- 备注:修改人:Ld 修改时间:2026-06-29 14:18:00 -->
<el-input v-model="production" style="width: 200px ; margin-left: 10px" size="small" clearable placeholder="物料名称/规格/型号" @keyup.enter.native="pageCurrent=1;pageSize=20;searchTable()"/><!-- 备注:修改人:Ld 修改时间:2026-06-29 14:18:00 -->
<span style="font-size: 17px;color: black ; margin-left: 24px">时间</span>
<el-date-picker
v-model="dateRange"
@@ -21,7 +22,7 @@
<div style="margin-top: 10px">
<el-table v-loading="loading" ref="table" :data="tableData" highlight-current-row show-summary border stripe size="small" style="width: 971px" height="740px">
<el-table-column width="70px" label="序号" type="index" align="center"/>
<el-table-column width="130px" label="物料编号" align="center" show-overflow-tooltip>
<el-table-column width="130px" label="物料编码" align="center" show-overflow-tooltip><!-- 备注:修改人:Ld 修改时间:2026-06-29 14:31:00 -->
<template slot-scope="scope">
{{ scope.row.物料编码 }}
</template>
@@ -83,6 +84,7 @@ export default {
data() {
return {
dateRange: '', // 提交日期
materialCodeValue: '', // 物料编码 备注:修改人:Ld 修改时间:2026-06-29 14:18:00
production: '',
loading: false, // 数据加载
total: 0, // 分页总数
@@ -108,15 +110,18 @@ export default {
// 导出表 8.10
exportExcel() {
if (this.tableData.length !== 0) {
let dateRange_check, production_check
let dateRange_check, materialCodeValue_check, production_check
this.dateRange ? dateRange_check = 1 : (dateRange_check = 0, this.dateRange = '')
this.materialCodeValue ? materialCodeValue_check = 1 : materialCodeValue_check = 0 // 备注:修改人:Ld 修改时间:2026-06-29 14:18:00
this.production ? production_check = 1 : production_check = 0
var param = []
param[0] = ['物料名称_check', production_check]
param[1] = ['物料名称', this.production]
param[2] = ['时间_check', dateRange_check]
param[3] = ['开始时间', this.dateRange[0]]
param[4] = ['结束时间', this.dateRange[1]]
param[2] = ['物料编码_check', materialCodeValue_check] // 备注:修改人:Ld 修改时间:2026-06-29 14:18:00
param[3] = ['物料编码', this.materialCodeValue] // 备注:修改人:Ld 修改时间:2026-06-29 14:18:00
param[4] = ['时间_check', dateRange_check]
param[5] = ['开始时间', this.dateRange[0]]
param[6] = ['结束时间', this.dateRange[1]]
var Data = this.CreateData('2001', '报表_仓储管理_盘点记录_查询', param)
this.exportExcel_NPOI(Data)
} else {
@@ -124,15 +129,18 @@ export default {
}
},
searchTable() {
let dateRange_check, production_check
let dateRange_check, materialCodeValue_check, production_check
this.dateRange ? dateRange_check = 1 : (dateRange_check = 0, this.dateRange = '')
this.materialCodeValue ? materialCodeValue_check = 1 : materialCodeValue_check = 0 // 备注:修改人:Ld 修改时间:2026-06-29 14:18:00
this.production ? production_check = 1 : production_check = 0
var param = []
param[0] = ['物料名称_check', production_check]
param[1] = ['物料名称', this.production]
param[2] = ['时间_check', dateRange_check]
param[3] = ['开始时间', this.dateRange[0]]
param[4] = ['结束时间', this.dateRange[1]]
param[2] = ['物料编码_check', materialCodeValue_check] // 备注:修改人:Ld 修改时间:2026-06-29 14:18:00
param[3] = ['物料编码', this.materialCodeValue] // 备注:修改人:Ld 修改时间:2026-06-29 14:18:00
param[4] = ['时间_check', dateRange_check]
param[5] = ['开始时间', this.dateRange[0]]
param[6] = ['结束时间', this.dateRange[1]]
var Data = this.CreateData('11', '仓储管理_盘点记录_查询', param, this.pageSize, this.pageCurrent)
this.ExecDatabase(Data).then(response => {
this.tableData = response.data.rows
@@ -156,4 +164,3 @@ export default {
width: 50px!important;
}
</style>

View File

@@ -2,7 +2,7 @@
<div class="app-container">
<el-card style="height: 850px">
<div style="margin-top: 7px; margin-bottom: 7px">
<el-input v-model="MaterialCode" style="width: 160px " size="small" clearable placeholder="物料编号"/>
<el-input v-model="MaterialCode" style="width: 160px " size="small" clearable placeholder="物料编码"/><!-- 备注:修改人:Ld 修改时间:2026-06-29 14:24:00 -->
<el-input v-model="MaterialName" style="width: 200px " size="small" clearable placeholder="物料名称/规格"/>
<el-input v-model="DrawingNo" style="width: 200px " size="small" clearable placeholder="图号/型号"/>
<el-input v-model="locationWarehouseValue" style="width:200px" placeholder="库位" size="small" clearable/>
@@ -11,7 +11,7 @@
<div>
<el-table v-loading="loading" :data="tableData" highlight-current-row show-summary border stripe size="small" style="width: 731px" height="740px">
<el-table-column :width="setColumnWidth('序号')" type="index" label="序号" align="center"/>
<el-table-column label="物料编号" align="center" show-overflow-tooltip>
<el-table-column label="物料编码" align="center" show-overflow-tooltip><!-- 备注:修改人:Ld 修改时间:2026-06-29 14:24:00 -->
<template slot-scope="scope">
{{ scope.row.物料编号 }}
</template>

View File

@@ -2,7 +2,8 @@
<div class="app-container">
<el-card style="height: 850px">
<div style="margin-top: 7px">
<el-input v-model="production" style="width: 200px ; margin-left: 10px" size="small" clearable placeholder="物料名称/编号/规格/型号" @keyup.enter.native="pageCurrent=1;pageSize=20;searchTable();searchNumber()"/>
<el-input v-model="materialCodeValue" style="width: 160px ; margin-left: 10px" size="small" clearable placeholder="物料编码" @keyup.enter.native="pageCurrent=1;pageSize=20;searchTable();searchNumber()"/><!-- 备注:修改人:Ld 修改时间:2026-06-29 14:18:00 -->
<el-input v-model="production" style="width: 200px ; margin-left: 10px" size="small" clearable placeholder="物料名称/规格/型号" @keyup.enter.native="pageCurrent=1;pageSize=20;searchTable();searchNumber()"/><!-- 备注:修改人:Ld 修改时间:2026-06-29 14:18:00 -->
<span style="font-size: 17px;color: black ; margin-left: 24px">时间</span>
<el-date-picker
v-model="dateRange"
@@ -32,7 +33,7 @@
style="width: 1061px"
height="740px">
<el-table-column width="70px" label="序号" type="index" align="center"/>
<el-table-column width="130px" label="物料编号" align="center" show-overflow-tooltip>
<el-table-column width="130px" label="物料编码" align="center" show-overflow-tooltip><!-- 备注:修改人:Ld 修改时间:2026-06-29 14:31:00 -->
<template slot-scope="scope">
{{ scope.row.物料编码 }}
</template>
@@ -99,6 +100,7 @@ export default {
data() {
return {
dateRange: '', // 提交日期
materialCodeValue: '', // 物料编码 备注:修改人:Ld 修改时间:2026-06-29 14:18:00
production: '',
loading: false, // 数据加载
stockInQuantity: 0, // 入库数
@@ -127,15 +129,18 @@ export default {
// 导出表 8.10
exportExcel() {
if (this.tableData.length !== 0) {
let dateRange_check, production_check
let dateRange_check, materialCodeValue_check, production_check
this.dateRange ? dateRange_check = 1 : (dateRange_check = 0, this.dateRange = '')
this.materialCodeValue ? materialCodeValue_check = 1 : materialCodeValue_check = 0 // 备注:修改人:Ld 修改时间:2026-06-29 14:18:00
this.production ? production_check = 1 : production_check = 0
var param = []
param[0] = ['物料名称_check', production_check]
param[1] = ['物料名称', this.production]
param[2] = ['时间_check', dateRange_check]
param[3] = ['开始时间', this.dateRange[0]]
param[4] = ['结束时间', this.dateRange[1]]
param[2] = ['物料编码_check', materialCodeValue_check] // 备注:修改人:Ld 修改时间:2026-06-29 14:18:00
param[3] = ['物料编码', this.materialCodeValue] // 备注:修改人:Ld 修改时间:2026-06-29 14:18:00
param[4] = ['时间_check', dateRange_check]
param[5] = ['开始时间', this.dateRange[0]]
param[6] = ['结束时间', this.dateRange[1]]
var Data = this.CreateData('2001', '报表_仓储管理_物料记录_查询', param)
this.exportExcel_NPOI(Data)
} else {
@@ -144,8 +149,9 @@ export default {
},
searchTable() {
if (this.dateRange) {
let dateRange_check, production_check
let dateRange_check, materialCodeValue_check, production_check
this.dateRange ? dateRange_check = 1 : (dateRange_check = 0, this.dateRange = '')
this.materialCodeValue ? materialCodeValue_check = 1 : materialCodeValue_check = 0 // 备注:修改人:Ld 修改时间:2026-06-29 14:18:00
this.production ? production_check = 1 : production_check = 0
// var param = []
// param[0] = ['物料名称_check', production_check]
@@ -166,9 +172,11 @@ export default {
var param = []
param[0] = ['物料名称_check', production_check]
param[1] = ['物料名称', this.production]
param[2] = ['时间_check', dateRange_check]
param[3] = ['开始时间', this.dateRange[0]]
param[4] = ['结束时间', this.dateRange[1]]
param[2] = ['物料编码_check', materialCodeValue_check] // 备注:修改人:Ld 修改时间:2026-06-29 14:18:00
param[3] = ['物料编码', this.materialCodeValue] // 备注:修改人:Ld 修改时间:2026-06-29 14:18:00
param[4] = ['时间_check', dateRange_check]
param[5] = ['开始时间', this.dateRange[0]]
param[6] = ['结束时间', this.dateRange[1]]
var Data = this.CreateData('11', '仓储管理_物料记录_查询新', param, this.pageSize, this.pageCurrent)
this.ExecDatabase(Data).then(response => {
console.log(response, 6666)
@@ -180,15 +188,18 @@ export default {
}
},
searchNumber() {
let dateRange_check, production_check
let dateRange_check, materialCodeValue_check, production_check
this.dateRange ? dateRange_check = 1 : (dateRange_check = 0, this.dateRange = '')
this.materialCodeValue ? materialCodeValue_check = 1 : materialCodeValue_check = 0 // 备注:修改人:Ld 修改时间:2026-06-29 14:18:00
this.production ? production_check = 1 : production_check = 0
var param = []
param[0] = ['物料名称_check', production_check]
param[1] = ['物料名称', this.production]
param[2] = ['时间_check', dateRange_check]
param[3] = ['开始时间', this.dateRange[0]]
param[4] = ['结束时间', this.dateRange[1]]
param[2] = ['物料编码_check', materialCodeValue_check] // 备注:修改人:Ld 修改时间:2026-06-29 14:18:00
param[3] = ['物料编码', this.materialCodeValue] // 备注:修改人:Ld 修改时间:2026-06-29 14:18:00
param[4] = ['时间_check', dateRange_check]
param[5] = ['开始时间', this.dateRange[0]]
param[6] = ['结束时间', this.dateRange[1]]
var Data = this.CreateData('11', '仓储管理_物料记录_查询', param)
this.ExecDatabase(Data).then(response => {
this.stockInQuantity = response.data[0].入库数
@@ -230,4 +241,3 @@ export default {
width: 50px!important;
}
</style>

View File

@@ -2,7 +2,8 @@
<div class="app-container">
<el-card style="height: 850px">
<div style="margin-top: 7px">
<el-input v-model="production" style="width: 200px ; margin-left: 10px" size="small" clearable placeholder="物料名称/编号/规格/型号" @keyup.enter.native="pageCurrent=1;pageSize=20;searchTable()"/>
<el-input v-model="materialCodeValue" style="width: 160px ; margin-left: 10px" size="small" clearable placeholder="物料编码" @keyup.enter.native="pageCurrent=1;pageSize=20;searchTable()"/><!-- 备注:修改人:Ld 修改时间:2026-06-29 14:18:00 -->
<el-input v-model="production" style="width: 200px ; margin-left: 10px" size="small" clearable placeholder="物料名称/规格/型号" @keyup.enter.native="pageCurrent=1;pageSize=20;searchTable()"/><!-- 备注:修改人:Ld 修改时间:2026-06-29 14:18:00 -->
<el-input v-model="purchaseNumber" style="width: 200px ; margin-left: 10px" size="small" clearable placeholder="采购合同编号" @keyup.enter.native="pageCurrent=1;pageSize=20;searchTable()"/>
<el-input v-model="supplier" style="width: 200px ; margin-left: 10px" size="small" clearable placeholder="供应商" @keyup.enter.native="pageCurrent=1;pageSize=20;searchTable()"/>
<span style="font-size: 17px;color: black ; margin-left: 10px">时间</span>
@@ -245,6 +246,7 @@ export default {
return {
NowTime: getNowDAY(),
dateRange: '', // 提交日期
materialCodeValue: '', // 物料编码 备注:修改人:Ld 修改时间:2026-06-29 14:18:00
production: '',
purchaseNumber: '',
supplier: '',
@@ -469,8 +471,9 @@ export default {
}
},
searchTable() {
let dateRange_check, production_check, purchaseNumber_check, supplier_check
let dateRange_check, materialCodeValue_check, production_check, purchaseNumber_check, supplier_check
this.dateRange ? dateRange_check = 1 : (dateRange_check = 0, this.dateRange = '')
this.materialCodeValue ? materialCodeValue_check = 1 : materialCodeValue_check = 0 // 备注:修改人:Ld 修改时间:2026-06-29 14:18:00
this.production ? production_check = 1 : production_check = 0
this.purchaseNumber ? purchaseNumber_check = 1 : purchaseNumber_check = 0
this.supplier ? supplier_check = 1 : supplier_check = 0
@@ -481,9 +484,11 @@ export default {
param[3] = ['采购合同', this.purchaseNumber]
param[4] = ['供应商_check', supplier_check]
param[5] = ['供应商', this.supplier]
param[6] = ['时间_check', dateRange_check]
param[7] = ['开始时间', this.dateRange[0]]
param[8] = ['结束时间', this.dateRange[1]]
param[6] = ['物料编码_check', materialCodeValue_check] // 备注:修改人:Ld 修改时间:2026-06-29 14:18:00
param[7] = ['物料编码', this.materialCodeValue] // 备注:修改人:Ld 修改时间:2026-06-29 14:18:00
param[8] = ['时间_check', dateRange_check]
param[9] = ['开始时间', this.dateRange[0]]
param[10] = ['结束时间', this.dateRange[1]]
var Data = this.CreateData('11', '仓储管理_入库记录退货_条件查询', param, this.pageSize, this.pageCurrent)
this.ExecDatabase(Data).then(response => {
this.tableData = response.data.rows
@@ -507,4 +512,3 @@ export default {
width: 50px!important;
}
</style>

View File

@@ -0,0 +1,962 @@
<template>
<div class="app-container">
<el-card>
<!-- 顶部筛选与操作区 -->
<div style="margin-top: 7px; margin-bottom: 7px">
<span style="font-size: 13px; color: black">排号筛选</span>
<el-select
v-model="rowFilter"
clearable
filterable
placeholder="选择排号"
size="small"
style="width: 120px; margin-right: 10px"
@change="handleRowFilterChange">
<el-option
v-for="item in rowOptions"
:key="item.value"
:label="item.label"
:value="item.value"/>
</el-select>
<span style="font-size: 13px; color: black; margin-left: 10px">库位类型</span>
<el-select
v-model="statusFilter"
clearable
filterable
placeholder="状态筛选"
size="small"
style="width: 120px; margin-right: 10px"
@change="handleStatusFilterChange">
<el-option
v-for="item in statusOptions"
:key="item.value"
:label="item.label"
:value="item.value"/>
</el-select>
<span style="font-size: 13px; color: black; margin-left: 10px">库位编号</span>
<el-input
v-model="locationCodeSearch"
placeholder="输入库位编号"
size="small"
clearable
style="width: 150px; margin-right: 10px"
@keyup.enter.native="handleSearch"/>
<el-button
type="primary"
size="small"
icon="el-icon-search"
style="margin-left: 10px"
@click="handleSearch">
搜索
</el-button>
<el-button
type="default"
size="small"
icon="el-icon-refresh"
style="margin-left: 10px"
@click="resetFilters">
重置
</el-button>
<el-button
type="success"
size="small"
icon="el-icon-refresh-left"
style="margin-left: 10px"
@click="refreshData">
刷新数据
</el-button>
<span style="font-size: 13px; color: black; margin-left: 20px">显示模式</span>
<el-switch
v-model="showCellText"
active-text="文字"
inactive-text="图标"
active-value="text"
inactive-value="icon"
style="margin-left: 5px"
@change="handleDisplayChange"/>
<span style="font-size: 13px; color: black; margin-left: 20px">自动刷新</span>
<el-switch
v-model="autoRefresh"
active-text="开"
inactive-text="关"
style="margin-left: 5px"
@change="handleAutoRefreshChange"/>
<el-select
v-if="autoRefresh"
v-model="refreshInterval"
size="small"
style="width: 90px; margin-left: 5px"
@change="handleRefreshIntervalChange">
<el-option
v-for="item in refreshOptions"
:key="item.value"
:label="item.label"
:value="item.value"/>
</el-select>
</div>
<!-- 统计信息卡片区 -->
<el-row :gutter="10" style="margin: 10px 0">
<el-col :span="4">
<el-card shadow="hover" style="text-align: center; padding: 10px 0">
<div style="color: #909399; font-size: 12px">总库位数</div>
<div style="color: #303133; font-size: 20px; font-weight: bold">{{ statistics.total }}</div>
</el-card>
</el-col>
<el-col :span="4">
<el-card shadow="hover" style="text-align: center; padding: 10px 0">
<div style="color: #909399; font-size: 12px">有库存</div>
<div style="color: #4caf50; font-size: 20px; font-weight: bold">{{ statistics.withStock }}</div>
</el-card>
</el-col>
<el-col :span="4">
<el-card shadow="hover" style="text-align: center; padding: 10px 0">
<div style="color: #909399; font-size: 12px">有物料/无库存</div>
<div style="color: #ff9800; font-size: 20px; font-weight: bold">{{ statistics.preOccupied }}</div>
</el-card>
</el-col>
<el-col :span="4">
<el-card shadow="hover" style="text-align: center; padding: 10px 0">
<div style="color: #909399; font-size: 12px">空闲库位</div>
<div style="color: #9e9e9e; font-size: 20px; font-weight: bold">{{ statistics.empty }}</div>
</el-card>
</el-col>
<el-col :span="4">
<el-card shadow="hover" style="text-align: center; padding: 10px 0">
<div style="color: #909399; font-size: 12px">使用率</div>
<div style="color: #2196f3; font-size: 20px; font-weight: bold">{{ statistics.usageRate }}%</div>
</el-card>
</el-col>
</el-row>
<!-- 状态图例区 -->
<div style="margin-bottom: 15px; display: flex; align-items: center; justify-content: center; gap: 20px; flex-wrap: wrap">
<div style="display: flex; align-items: center">
<div class="legend-color" style="background-color: #4caf50"/>
<span style="margin-left: 5px; font-size: 12px">有库存 (存)</span>
</div>
<div style="display: flex; align-items: center">
<div class="legend-color" style="background-color: #ff9800"/>
<span style="margin-left: 5px; font-size: 12px">有物料/无库存 (待)</span>
</div>
<div style="display: flex; align-items: center">
<div class="legend-color" style="background-color: #e0e0e0"/>
<span style="margin-left: 5px; font-size: 12px">空闲 (空)</span>
</div>
<div style="display: flex; align-items: center">
<div class="legend-color" style="background-color: #bbdefb; border: 2px solid #2196f3"/>
<span style="margin-left: 5px; font-size: 12px">选中库位</span>
</div>
</div>
<!-- 三楼仓库库位网格矩阵区 -->
<div v-loading="loading" style="height: 580px; overflow: auto; border: 1px solid #e4e7ed; border-radius: 4px; padding: 15px; background: #f5f7fa">
<!-- 数据为空提示 -->
<div v-if="groupedData.length === 0 && !loading" style="text-align: center; padding: 100px; color: #909399">
<span style="font-size: 14px">暂无库位数据</span>
</div>
<!-- 按排号分组的库位矩阵 -->
<div v-else>
<div v-for="group in groupedData" :key="group.排号" class="warehouse-group">
<div style="text-align: center; margin-bottom: 10px; color: #303133; font-weight: bold; background: #e8f4fd; padding: 5px; border-radius: 4px">
排号{{ group.排号 }} - 三楼仓库库位
<span style="margin-left: 20px; color: #909399; font-weight: normal; font-size: 12px">最后更新: {{ lastUpdateTime }}</span>
</div>
<div style="overflow-x: auto; margin-bottom: 10px">
<table class="warehouse-table">
<!-- 列标题 -->
<thead>
<tr>
<th class="table-header row-header">层号</th>
<th
v-for="col in columnKeys"
:key="col"
:class="{ 'highlighted-column': selectedCell && selectedCell.column === col && selectedCell.排号 === group.排号 }"
class="table-header column-header">
{{ col }}列
</th>
</tr>
</thead>
<!-- 数据行(层号) -->
<tbody>
<tr
v-for="layer in group.rows"
:key="layer.层号"
:class="{ 'highlighted-row': highlightedRow === layer.层号 && highlightedGroup === group.排号 }"
class="table-row">
<td class="row-label-cell">{{ layer.层号 }}层</td>
<td
v-for="col in columnKeys"
:key="col"
:data-row="group.排号 + '-' + layer.层号"
:data-col="col"
:class="[
'status-' + (layer.cells[col] ? layer.cells[col].状态 : -1),
{
'selected-cell': selectedCell &&
selectedCell.排号 === group.排号 &&
selectedCell.层号 === layer.层号 &&
selectedCell.column === col,
'search-highlight': isSearchHighlighted(group.排号, layer.层号, col)
}
]"
class="location-cell"
@click="selectLocation(group.排号, layer.层号, col, layer.cells[col])"
@mouseenter="onCellHover(group.排号, layer.层号, col)"
@mouseleave="onCellLeave">
<div class="cell-content">
<span v-if="showCellText === 'text'" class="cell-text">
{{ getStatusLabel(layer.cells[col] ? layer.cells[col].状态 : -1) }}
</span>
<span v-else class="cell-icon">
<i :class="getStatusIcon(layer.cells[col] ? layer.cells[col].状态 : -1)"/>
</span>
<div v-if="isHoveredCell(group.排号, layer.层号, col)" class="cell-tooltip">
{{ layer.cells[col] ? (layer.cells[col].库位编号 + ' - ' + getStatusDescription(layer.cells[col].状态)) : '无库位' }}
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- 库位详情弹窗 -->
<el-dialog
:visible.sync="detailDialogVisible"
:title="'库位详情 - ' + detailInfo.库位编号"
center
width="500px">
<el-descriptions :column="1" border size="small">
<el-descriptions-item label="库位编号">{{ detailInfo.库位编号 }}</el-descriptions-item>
<el-descriptions-item label="库位名称">{{ detailInfo.库位名称 }}</el-descriptions-item>
<el-descriptions-item label="排号">{{ detailInfo.排号 }}</el-descriptions-item>
<el-descriptions-item label="层号">{{ detailInfo.层号 }}</el-descriptions-item>
<el-descriptions-item label="当前状态">
<el-tag :type="getStatusTagType(detailInfo.状态值)" size="small">{{ detailInfo.状态 }}</el-tag>
</el-descriptions-item>
<el-descriptions-item label="状态描述">{{ detailInfo.状态描述 }}</el-descriptions-item>
</el-descriptions>
<div slot="footer" class="dialog-footer">
<el-button size="small" @click="detailDialogVisible = false">关闭</el-button>
</div>
</el-dialog>
<!-- 物料信息弹窗 -->
<el-dialog
:visible.sync="materialDialogVisible"
:title="'库位物料信息 - ' + materialLocationCode"
center
width="900px">
<el-table
v-loading="materialLoading"
:data="materialList"
stripe
size="small"
style="width: 100%"
height="350">
<el-table-column
prop="物料编号"
label="物料编号"
width="150"
show-overflow-tooltip/>
<el-table-column
prop="物料名称"
label="物料名称"
width="120"
show-overflow-tooltip/>
<el-table-column
prop="图号"
label="图号"
width="150"
show-overflow-tooltip/>
<el-table-column
prop="规格型号"
label="规格型号"
width="150"
show-overflow-tooltip/>
<el-table-column
label="库存数量"
width="100"
align="right">
<template slot-scope="scope">
<span :style="{ color: scope.row.库存数量 > 0 ? '#67C23A' : '#F56C6C', fontWeight: 'bold' }">
{{ formatNumber(scope.row.库存数量) }}
</span>
<div style="font-size: 10px; color: #909399">
{{ scope.row.单位 || '台' }}
</div>
</template>
</el-table-column>
</el-table>
<div v-if="materialList.length === 0 && !materialLoading" slot="append" style="text-align: center; padding: 20px; color: #909399">
暂无物料数据
</div>
<div slot="footer" class="dialog-footer">
<el-button size="small" @click="materialDialogVisible = false">关闭</el-button>
</div>
</el-dialog>
</el-card>
</div>
</template>
<script>
import { ExecDatabase } from '@/main'
export default {
name: 'ThirdFloorWarehouse',
data() {
return {
// ========== 筛选条件 ==========
rowFilter: '',
statusFilter: '',
locationCodeSearch: '',
rowOptions: [],
statusOptions: [
{ label: '全部', value: '' },
{ label: '有库存', value: 2 },
{ label: '有物料/无库存', value: 1 },
{ label: '空闲', value: 0 }
],
// ========== 自动刷新 ==========
autoRefresh: false,
refreshInterval: 60,
refreshTimer: null,
refreshOptions: [
{ label: '30秒', value: 30 },
{ label: '1分钟', value: 60 },
{ label: '5分钟', value: 300 }
],
// ========== 矩阵数据 ==========
locationRawData: [],
allLayers: [],
columnKeys: [],
groupedData: [],
// ========== 选中与交互状态 ==========
selectedCell: null,
highlightedRow: null,
highlightedGroup: null,
hoveredCell: null,
// ========== 统计信息 ==========
statistics: {
total: 0,
withStock: 0,
preOccupied: 0,
empty: 0,
usageRate: 0
},
// ========== 弹窗 ==========
detailDialogVisible: false,
detailInfo: {},
materialDialogVisible: false,
materialLoading: false,
materialList: [],
materialLocationCode: '',
// ========== 显示设置 ==========
showCellText: 'text',
loading: false,
lastUpdateTime: ''
}
},
created() {
this.loadLocationData()
},
beforeDestroy() {
this.stopAutoRefresh()
},
methods: {
// ========== 数据加载与转换 ==========
loadLocationData() {
var self = this
self.loading = true
var param = []
param[0] = ['排号_check', self.rowFilter ? 1 : 0]
param[1] = ['排号', self.rowFilter]
param[2] = ['库位编号_check', self.locationCodeSearch ? 1 : 0]
param[3] = ['库位编号', self.locationCodeSearch]
param[4] = ['库位状态_check', self.statusFilter !== '' ? 1 : 0]
param[5] = ['库位状态', self.statusFilter]
var Data = self.CreateData('11', '仓储管理_三楼仓库库位_查询数据', param)
ExecDatabase(Data).then(function(response) {
var rows = []
if (response.data && response.data.rows) {
rows = response.data.rows
} else if (response.data && Array.isArray(response.data)) {
rows = response.data
}
self.locationRawData = rows
self.buildMatrix(rows)
self.updateRowOptions()
self.updateLastUpdateTime()
self.loading = false
}).catch(function() {
self.loading = false
})
},
// 构建矩阵:固定8排24列3层,全空也显示
buildMatrix(list) {
var self = this
// 固定维度:8排 24列 3层
var rowKeys = ['01', '02', '03', '04', '05', '06', '07', '08']
var layers = ['03', '02', '01']
var cols = []
for (var i = 1; i <= 24; i++) {
cols.push(i)
}
self.allLayers = layers
self.columnKeys = cols
// 按排号-层号-列号索引实际数据
var dataMap = {}
list.forEach(function(item) {
var key = item.排号 + '-' + item.层号 + '-' + item.列号
dataMap[key] = {
库位编号: item.库位编号,
库位名称: item.库位编号,
状态: item.状态,
排号: item.排号,
层号: item.层号,
列号: item.列号
}
})
// 构建分组数据(8排全部显示)
var grouped = []
rowKeys.forEach(function(rowKey) {
var rows = []
layers.forEach(function(layer) {
var layerCells = {}
cols.forEach(function(col) {
var key = rowKey + '-' + layer + '-' + col
if (dataMap[key]) {
layerCells[col] = dataMap[key]
} else {
layerCells[col] = {
库位编号: rowKey + '-' + (col < 10 ? '0' + col : col) + '-' + layer,
库位名称: rowKey + '-' + (col < 10 ? '0' + col : col) + '-' + layer,
状态: 0,
排号: rowKey,
层号: layer,
列号: col
}
}
})
rows.push({ 层号: layer, cells: layerCells })
})
grouped.push({ 排号: rowKey, rows: rows })
})
self.groupedData = grouped
self.updateStatistics()
},
updateRowOptions() {
var self = this
var rowSet = {}
self.locationRawData.forEach(function(item) {
if (item.排号) {
rowSet[item.排号] = true
}
})
var rows = Object.keys(rowSet).sort()
self.rowOptions = rows.map(function(r) {
return { label: r + '排', value: r }
})
},
updateLastUpdateTime() {
this.lastUpdateTime = new Date().toLocaleString()
},
// ========== 筛选与搜索 ==========
handleRowFilterChange() {
this.loadLocationData()
},
handleStatusFilterChange() {
this.loadLocationData()
},
handleSearch() {
this.loadLocationData()
},
resetFilters() {
this.rowFilter = ''
this.statusFilter = ''
this.locationCodeSearch = ''
this.loadLocationData()
this.$message.info('筛选条件已重置')
},
refreshData() {
this.loadLocationData()
},
// ========== 统计计算 ==========
updateStatistics() {
var withStock = 0
var preOccupied = 0
var empty = 0
this.groupedData.forEach(function(group) {
group.rows.forEach(function(layer) {
Object.keys(layer.cells).forEach(function(colKey) {
var cell = layer.cells[colKey]
if (!cell) return
var status = cell.状态
if (status === 2) withStock++
else if (status === 1) preOccupied++
else if (status === 0) empty++
})
})
})
var total = withStock + preOccupied + empty
var usageRate = total > 0 ? (((withStock + preOccupied) / total) * 100).toFixed(1) : 0
this.statistics = { total: total, withStock: withStock, preOccupied: preOccupied, empty: empty, usageRate: usageRate }
},
// ========== 单元格交互 ==========
selectLocation(rowKey, layerKey, colKey, locationInfo) {
this.selectedCell = { 排号: rowKey, 层号: layerKey, column: colKey }
this.highlightedRow = layerKey
this.highlightedGroup = rowKey
if (!locationInfo) return
if (locationInfo.状态 === 2) {
this.loadMaterialDetail(locationInfo.库位编号)
} else {
this.showLocationDetail(locationInfo)
}
},
loadMaterialDetail(locationCode) {
var self = this
self.materialLoading = true
self.materialLocationCode = locationCode
self.materialList = []
var param = []
param[0] = ['库位编号_check', 1]
param[1] = ['库位编号', locationCode]
var Data = self.CreateData('11', '仓储管理_三楼仓库库位物料_查询数据', param)
ExecDatabase(Data).then(function(response) {
var rows = []
if (response.data && response.data.rows) {
rows = response.data.rows
} else if (response.data && Array.isArray(response.data)) {
rows = response.data
}
// 按物料编号合并,库存数量累加
var merged = {}
rows.forEach(function(item) {
var key = item.物料编号
if (!merged[key]) {
merged[key] = {
物料编号: item.物料编号,
物料名称: item.物料名称,
图号: item.图号,
规格型号: item.规格型号,
库存数量: 0,
单位: item.单位
}
}
merged[key].库存数量 += parseFloat(item.库存数量) || 0
})
self.materialList = Object.values(merged)
self.materialDialogVisible = true
self.materialLoading = false
}).catch(function() {
self.materialList = []
self.materialDialogVisible = true
self.materialLoading = false
})
},
showLocationDetail(locationInfo) {
this.detailInfo = {
库位编号: locationInfo.库位编号,
库位名称: locationInfo.库位名称,
排号: locationInfo.排号,
层号: locationInfo.层号,
状态: this.getStatusLabel(locationInfo.状态),
状态值: locationInfo.状态,
状态描述: this.getStatusDescription(locationInfo.状态)
}
this.detailDialogVisible = true
},
onCellHover(rowKey, layerKey, colKey) {
this.hoveredCell = { 排号: rowKey, 层号: layerKey, column: colKey }
},
onCellLeave() {
this.hoveredCell = null
},
isHoveredCell(rowKey, layerKey, colKey) {
return this.hoveredCell &&
this.hoveredCell.排号 === rowKey &&
this.hoveredCell.层号 === layerKey &&
this.hoveredCell.column === colKey
},
isSearchHighlighted(rowKey, layerKey, colKey) {
if (!this.locationCodeSearch || !this.selectedCell) return false
return this.selectedCell.排号 === rowKey &&
this.selectedCell.层号 === layerKey &&
this.selectedCell.column === colKey
},
// ========== 状态映射方法 ==========
getStatusLabel(status) {
var map = { 0: '空', 1: '待', 2: '存' }
return map[status] || '-'
},
getStatusDescription(status) {
var map = {
0: '空闲库位,可存放物料',
1: '有物料但无库存',
2: '有库存,库位上存放有物料'
}
return map[status] || '无库位'
},
getStatusIcon(status) {
var map = {
0: 'el-icon-minus',
1: 'el-icon-time',
2: 'el-icon-box'
}
return map[status] || 'el-icon-question'
},
getStatusTagType(status) {
var map = {
0: 'info',
1: 'warning',
2: 'success'
}
return map[status] || 'info'
},
formatNumber(num) {
var n = parseFloat(num)
if (isNaN(n)) return '0.000'
return n.toFixed(3)
},
// ========== 显示模式 ==========
handleDisplayChange() {
this.$message.info('已切换为' + (this.showCellText === 'text' ? '显示文字' : '显示图标') + '模式')
},
// ========== 自动刷新 ==========
handleAutoRefreshChange(val) {
if (val) {
this.startAutoRefresh()
} else {
this.stopAutoRefresh()
}
},
startAutoRefresh() {
var self = this
self.stopAutoRefresh()
self.refreshTimer = setInterval(function() {
self.loadLocationData()
}, self.refreshInterval * 1000)
},
stopAutoRefresh() {
if (this.refreshTimer) {
clearInterval(this.refreshTimer)
this.refreshTimer = null
}
},
handleRefreshIntervalChange() {
if (this.autoRefresh) {
this.startAutoRefresh()
}
}
}
}
</script>
<style lang="scss" scoped>
.app-container {
padding: 20px;
}
/deep/ .el-card__body {
padding: 15px !important;
}
.legend-color {
width: 20px;
height: 20px;
border-radius: 4px;
border: 1px solid #ddd;
}
.warehouse-group {
margin-bottom: 20px;
padding: 15px;
background: white;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.warehouse-table {
width: 100%;
border-collapse: collapse;
margin: 0 auto;
min-width: 1200px;
.table-header {
background-color: #f5f7fa;
color: #303133;
font-weight: bold;
text-align: center;
padding: 8px;
border: 1px solid #e4e7ed;
min-width: 40px;
height: 40px;
font-size: 12px;
&.row-header {
background-color: #e8f4fd;
color: #2196f3;
min-width: 80px;
font-size: 12px;
}
&.highlighted-column {
background-color: #bbdefb;
color: #1976d2;
font-weight: bold;
}
}
.table-row {
&.highlighted-row {
background-color: #f0f9ff;
.row-label-cell {
background-color: #bbdefb;
color: #1976d2;
font-weight: bold;
}
}
&:hover {
background-color: #f5f7fa;
}
}
.row-label-cell {
background-color: #f5f7fa;
color: #303133;
font-weight: bold;
text-align: center;
padding: 8px;
border: 1px solid #e4e7ed;
width: 80px;
font-size: 12px;
}
.location-cell {
position: relative;
text-align: center;
padding: 0;
border: 1px solid #e4e7ed;
cursor: pointer;
transition: all 0.2s;
min-width: 40px;
height: 40px;
&:hover {
transform: scale(1.1);
z-index: 10;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
}
&.selected-cell {
box-shadow: 0 0 0 2px #2196f3;
z-index: 20;
position: relative;
&::after {
content: '';
position: absolute;
top: -2px;
left: -2px;
right: -2px;
bottom: -2px;
border: 2px solid #2196f3;
border-radius: 2px;
pointer-events: none;
}
}
&.search-highlight {
animation: highlightPulse 1.5s ease-in-out 3;
}
&.status-2 {
background-color: #4caf50;
color: white;
&:hover {
background-color: #388e3c;
}
}
&.status-1 {
background-color: #ff9800;
color: white;
&:hover {
background-color: #f57c00;
}
}
&.status-0 {
background-color: #e0e0e0;
color: #666;
&:hover {
background-color: #bdbdbd;
}
}
&.status--1 {
background-color: #fafafa;
color: #ccc;
}
.cell-content {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
position: relative;
}
.cell-text {
font-size: 14px;
font-weight: bold;
}
.cell-icon {
font-size: 18px;
}
.cell-tooltip {
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%);
background-color: rgba(0, 0, 0, 0.8);
color: white;
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
white-space: nowrap;
z-index: 30;
margin-bottom: 5px;
&::after {
content: '';
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
border-width: 5px;
border-style: solid;
border-color: rgba(0, 0, 0, 0.8) transparent transparent transparent;
}
}
}
}
@keyframes highlightPulse {
0% { box-shadow: 0 0 0 0 rgba(33, 150, 243, 0.4); }
50% { box-shadow: 0 0 0 6px rgba(33, 150, 243, 0.2); }
100% { box-shadow: 0 0 0 0 rgba(33, 150, 243, 0); }
}
// 响应式调整
@media (max-width: 1400px) {
.warehouse-table {
min-width: 900px;
.location-cell {
min-width: 35px;
height: 35px;
.cell-text {
font-size: 12px;
}
.cell-icon {
font-size: 16px;
}
}
}
}
@media (max-width: 992px) {
.warehouse-table {
min-width: 700px;
.location-cell {
min-width: 30px;
height: 30px;
.cell-text {
font-size: 11px;
}
.cell-icon {
font-size: 14px;
}
}
.row-label-cell {
width: 60px;
font-size: 10px;
padding: 4px;
}
}
}
</style>

View File

@@ -12,7 +12,7 @@
<el-input
v-model="materialCodeValue"
clearable
placeholder="物料编号"
placeholder="物料编码"
size="small"
style="width:160px"
@keyup.enter.native="searchTable()"/>
@@ -134,7 +134,7 @@
</el-table-column>
<el-table-column
align="center"
label="物料编号"
label="物料编码"
prop="物料码"
show-overflow-tooltip
sortable="物料码"
@@ -519,7 +519,7 @@ export default {
returns: [],
contractNumber: '', // 合同
supplierSelectionName: '', // 选择供应商名称
materialCodeValue: '', // 物料编号
materialCodeValue: '', // 物料编码 备注:修改人:Ld 修改时间:2026-06-29 14:24:00
materialNameTypeValue: '', // 物料名称规格型号
materialNameType: [], // 物料名称规格型号数组
入库单号Value: '', // 入库单号