BaoKaiWms_202506-Wms-YaXinKe/wms_client/src/layout/stock.vue

566 lines
20 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div style="margin-bottom: 10px">
<el-config-provider :locale="zhCn">
<el-row>
<el-input v-model="queryKey" v-if="dataType == 1" style="width: 256px; margin-right: 10px;"
placeholder="零件号/箱号/零件名称" :suffix-icon="Search" />
<el-input v-if="dataType == 2" v-model="goodsIdQuery" style="width: 256px; margin-right: 10px;"
placeholder="零件号" :suffix-icon="Search" />
<el-button type="primary" @click="search()">搜索</el-button>
<el-button type="warning" @click="reset()">重置</el-button>
<el-button type="success" @click="exportExcel()">导出表格</el-button>
<el-col :span="6" style="display: flex; align-items: center; justify-content: center; border: 1px solid blue; padding: 5px; border-radius: 4px;">
<span>当前物料总数量: {{ goodstotal }}</span>
</el-col>
<el-button type="success" @click="showVerticalStorageStats()" style="width: 120px; margin-left: 5px;">立库库位统计</el-button>
<el-button type="success" @click="showFourWayStorageStats()" style="width: 140px; margin-left: 5px;">四向车库库位统计</el-button>
</el-row>
<br />
<el-table id="stock-table" :data="displayStocks" stripe border v-loading="loading" class="table-class"
max-height="650px" highlight-current-row @row-click="getCurrentRow" :row-style="rowStyle"
:header-cell-style="{ 'text-align': 'center' }" :cell-style="{ 'text-align': 'center' }">
<el-table-column width="65px" fixed="left">
<template v-slot="scope">
<el-radio :label="scope.row.stockId" v-model="stockId">&nbsp;</el-radio>
</template>
</el-table-column>
<!-- <el-table-column prop="stockId" label="库存ID" min-width="120px" show-overflow-tooltip /> -->
<el-table-column prop="goodsId" label="物料号" min-width="120px" />
<el-table-column prop="batchNo" v-if="dataType == 1" label="批次号" min-width="120px" />
<el-table-column prop="availableNum" v-if="dataType == 1" label="可用数量" min-width="100px" />
<el-table-column prop="realNum" label="实际数量" min-width="100px" />
<el-table-column prop="vehicleId" v-if="dataType == 1" label="料箱号" fixed="left" min-width="120px" />
<el-table-column prop="productionDate" v-if="dataType == 1" label="生产日期" :formatter="dateFormat" min-width="140px" />
<el-table-column prop="locationId" v-if="dataType == 1" label="库位" :formatter="locationFormat" min-width="160px" />
<el-table-column prop="createTime" v-if="dataType == 1" label="上架时间" :formatter="timeFormat" min-width="140px" />
<!-- <el-table-column prop="goodsStatus" v-if="dataType == 1" label="物料状态" :formatter="goodsStatusFormat" min-width="120px" /> -->
<el-table-column prop="stockStatus" v-if="dataType == 1" label="库存状态" :formatter="stockStatusFormat" fixed="right"
min-width="120px" />
<el-table-column fixed="right" label="操作" width="135px" v-if="selStock == null">
<template v-slot="scope">
<el-button size="small" plain type="primary" @click="editCurrentRowStock(scope.row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
<br />
<el-pagination v-model:current-page="currentPage" v-model:page-size="pageSize" :page-sizes="[10, 25, 50]"
:small="false" :disabled="false" :background="false" :default-page-size="10" @size-change="search"
@current-change="search" layout="total, sizes, prev, pager, next, jumper" :total="total" />
<br />
<el-dialog v-model="dialogVisible" title="物料信息" width="40%" draggable :show-close="false">
<el-form ref="stockFormRef" :model="stockFormEntity" :label-position="labelPosition" label-width="100px"
style="max-width: 100%" :rules="rules" status-icon>
<el-row :gutter="16">
<el-col :span="12" :offset="0">
<el-form-item label="零件号" prop="goodsId">
<el-input v-model="stockFormEntity.goodsId" readonly />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="货箱号" prop="vehicleId">
<el-input v-model="stockFormEntity.vehicleId" clearable readonly />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12" :offset="0">
<el-form-item label="可用数量" prop="availableNum">
<el-input-number v-model.number="stockFormEntity.availableNum" clearable
controls-position="right" :min="0" />
</el-form-item>
</el-col>
<el-col :span="12" :offset="0">
<el-form-item label="实际数量为0时删除库存" prop="realNum">
<el-input-number v-model.number="stockFormEntity.realNum" controls-position="right"
:min="0" clearable />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
</el-row>
<el-row :gutter="16">
<el-col :span="12" :offset="0">
<el-form-item label="库存状态" prop="stockStatus">
<el-select-v2 v-model="stockFormEntity.stockStatus" placeholder="请选择库存状态"
:options="stockStatusOptions"></el-select-v2>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="物料状态" prop="goodsStatus">
<el-select-v2 v-model="stockFormEntity.goodsStatus" placeholder="请选择物料状态"
:options="goodsStatusOptions"></el-select-v2>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitStockInfo(stockFormEntity)">
确定
</el-button>
<el-button type="success" @click="submitStockInfo(stockFormEntity)">
确定
</el-button>
</span>
</template>
</el-dialog>
<!-- 新增库位统计弹窗 -->
<el-dialog v-model="locationStatsDialogVisible" :title="storageStatsTitle" width="40%" draggable>
<el-row :gutter="20" justify="center">
<el-col :span="10">
<div class="stat-card occupied">
<div class="stat-value">{{ locationStats.occupied }}</div>
<div class="stat-label">已占用库位</div>
</div>
</el-col>
<el-col :span="10">
<div class="stat-card free">
<div class="stat-value">{{ locationStats.free }}</div>
<div class="stat-label">空闲库位</div>
</div>
</el-col>
</el-row>
<el-row :gutter="20" justify="center" style="margin-top: 20px;">
<el-col :span="10">
<div class="stat-card total">
<div class="stat-value">{{ locationStats.total }}</div>
<div class="stat-label">总库位数</div>
</div>
</el-col>
</el-row>
</el-dialog>
</el-config-provider>
</div>
</template>
<script setup>
import { getAllStocks, updateStockInfo, getAllStocksByGoodsId } from '@/api/stock.js'
import { downloadStockExcel } from '@/api/excel.js'
import { dateFormatter, locationFormatter, timeFormatter } from '@/utils/formatter.js'
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
import { Search } from '@element-plus/icons-vue'
import { ref, reactive } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import apiOrderOut from '@/api/order.out'
</script>
<script>
export default {
name: 'stock',
props: ['selStock'],
emits: ['update:selStock'],
data() {
return {
pageInfo: {},
displayStocks: [],
currentPage: 1,
pageSize: 10,
total: 0,
goodstotal: 0,
queryKey: '',
loading: true,
stockId: '',
stockFormRef: ref(),
stockFormEntity: reactive({}),
rules: reactive({}),
labelPosition: 'top',
dialogVisible: false,
goodsStatusOptions: [
{
value: 0,
label: '合格'
},
{
value: 1,
label: '不合格'
},
{
value: 2,
label: '延期'
},
{
value: 3,
label: '过期'
},
{
value: 5,
label: '长时间未使用'
}
],
stockStatusOptions: [
{
value: 0,
label: '库存正常'
},
{
value: 1,
label: '准备出库'
},
{
value: 2,
label: '正在出库'
},
{
value: 3,
label: '出库完成,环线运输中'
},
{
value: 4,
label: '站台拣货中'
},
{
value: 5,
label: '站台盘点中'
},
{
value: 6,
label: '正在回库'
},
{
value: 9,
label: '库存锁定'
}
],
dataType: 1,
dataTypeOptions: [
{
value: 1,
label: '按料箱分类'
},
{
value: 2,
label: '按零件分类'
}
],
goodsIdQuery: '',
// 新增库位统计数据
locationStatsDialogVisible: false,
storageStatsTitle: '库位统计', // 新增标题数据
locationStats: {
occupied: 0,
free: 0,
total: 0
}
}
},
mounted() {
this.refresh()
},
methods: {
locationFormat: (row, column, cellValue, index) => {
return locationFormatter(cellValue)
},
dateFormat: (row, column, cellValue, index) => {
return dateFormatter(cellValue)
},
timeFormat: (row, column, cellValue, index) => {
return timeFormatter(cellValue)
},
goodsStatusFormat: (row, column, cellValue, index) => {
if (cellValue === 0) {
return '合格'
} else if (cellValue === 1) {
return '不合格'
} else if (cellValue === 2) {
return '延期'
} else if (cellValue === 3) {
return '过期'
} else if (cellValue === 5) {
return '长时间未使用'
}
},
stockStatusFormat: (row, column, cellValue, index) => {
switch (cellValue) {
case 0:
return '库存正常'
case 1:
return '准备出库'
case 2:
return '正在出库'
case 3:
return '出库完成,环线运输中'
case 4:
return '站台拣货中'
case 5:
return '站台盘点中'
case 6:
return '正在回库'
case 7:
return '到达拣选站台'
case 9:
return '库存锁定'
default:
return '异常'
}
},
rowStyle: ({ row, rowIndex }) => {
if (row.goodsStatus == 3) {
return { "color": "red" }
}
if (row.goodsStatus == 5) {
return { "color": "yellow" }
}
},
search() {
this.loading = true
this.pageInfo.pageNum = this.currentPage
this.pageInfo.pageSize = this.pageSize
if (this.dataType == 1) {
const tableRequest = {
page: this.pageInfo,
param: this.queryKey.trim(),
}
getAllStocks(tableRequest).then(res => {
const tableResponse = res.data
if (tableResponse.code != 0) {
ElMessage.error(tableResponse.message)
}
this.displayStocks = tableResponse.rows
this.total = tableResponse.total
if (this.queryKey !== '') {
this.calculateGoodstotal()
}else{
this.goodstotal = 0
}
}).catch(err => {
console.log(err)
ElMessage.error('查询库存错误')
})
} else if (this.dataType == 2) {
const tableRequest = {
page: this.pageInfo,
param: this.goodsIdQuery.trim(),
}
getAllStocksByGoodsId(tableRequest).then(res => {
const tableResponse = res.data
if (tableResponse.code != 0) {
ElMessage.error(tableResponse.message)
}
this.displayStocks = tableResponse.rows
this.total = tableResponse.total
if (this.queryKey !== '') {
this.calculateGoodstotal()
}
}).catch(err => {
console.log(err)
ElMessage.error('查询库存错误')
})
}
this.loading = false
},
calculateGoodstotal() {
this.goodstotal = this.displayStocks.reduce((sum, stock) => sum + stock.realNum, 0)
},
reset() {
this.queryKey = ''
this.goodsIdQuery = ''
this.search()
},
refresh() {
this.search()
},
getCurrentRow(row) {
this.stockId = row.stockId
this.$emit('update:selStock', row)
},
exportExcel() {
downloadStockExcel().then(res => {
const link = document.createElement('a');//创建a标签
try {
let blob = res.data
let _fileName = "库存报表" + dateFormatter(new Date) + ".xlsx"
link.style.display = 'none'//隐藏
const url = window.URL || window.webkitURL || window.moxURL
link.href = url.createObjectURL(blob)
link.setAttribute('download', _fileName.substring(_fileName.lastIndexOf('_') + 1))
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
url.revokeObjectURL(link.href)//移除url对象
} catch (e) {
ElMessage({
message: '下载文件失败:: ' + e,
type: 'error',
showClose: true
})
}
}).catch(err => {
ElMessage({
message: '导出失败:: ' + err,
type: 'error',
showClose: true
})
})
},
editCurrentRowStock(row) {
this.stockFormEntity = row
this.dialogVisible = true
},
outRowStock(row) {
const stockId = row.stockId;
ElMessageBox.confirm(`是否执行 ${stockId} 的出库?`, '提示')
.then(() => {
apiOrderOut.outRowStock(stockId).then(res => {
const responseData = res.data;
if (responseData.code === 0) {
ElMessage({
message: '执行成功',
type: 'success',
});
this.queryGoodsOrderOut();
} else {
ElMessageBox.alert(`服务器返回失败:${responseData.message}`, '执行失败', {
type: 'warning',
confirmButtonText: '确定',
showClose: false
})
}
}).catch(err => {
ElMessageBox.alert(`请求服务器失败::${err}`, '任务添加失败', {
type: 'warning',
confirmButtonText: '确定'
})
})
})
.catch(() => {
})
},
submitStockInfo(stockFormEntity) {
updateStockInfo(stockFormEntity).then(res => {
if (res.data.code == 0) {
this.dialogVisible = false
ElMessage({
message: '更新库存成功',
type: 'success'
})
this.search()
} else {
ElMessage.error(res.data.message)
}
}).catch(err => {
ElMessage({
message: '更新料箱信息失败: ' + err,
type: 'error',
showClose: true
})
})
},
// 立库统计方法
// 修改 showVerticalStorageStats 方法
async showVerticalStorageStats() {
try {
this.storageStatsTitle = '立库统计';
// 调用获取立库统计的API
const response = await fetch('http://172.21.80.150:19990/api/query/getAllLocations');
const data = await response.json();
// 解析数据
const occupied = data.find(item => item.name === '占用')?.value || 0;
const free = data.find(item => item.name === '空闲')?.value || 0;
const locked = data.find(item => item.name === '锁定')?.value || 0;
const total = occupied + free + locked;
this.locationStats = {
occupied: occupied,
free: free,
total: total
};
this.locationStatsDialogVisible = true;
} catch (error) {
ElMessage.error("获取立库统计信息失败: " + error.message);
}
},
// 修改 showFourWayStorageStats 方法
async showFourWayStorageStats() {
try {
this.storageStatsTitle = '四向车库统计';
// 调用获取四向车库统计的API
// 注意这里假设四向车库使用相同的接口如果有不同的接口请相应修改URL
const response = await fetch('http://172.21.80.150:19990/api/query/getAllLocationsForFour');
const data = await response.json();
// 解析数据(这里使用相同的数据结构,实际项目中可能需要根据参数区分)
const occupied = data.find(item => item.name === '占用')?.value || 0;
const free = data.find(item => item.name === '空闲')?.value || 0;
const locked = data.find(item => item.name === '锁定')?.value || 0;
const total = occupied + free + locked;
this.locationStats = {
occupied: occupied,
free: free,
total: total
};
this.locationStatsDialogVisible = true;
} catch (error) {
ElMessage.error("获取四向车库统计信息失败: " + error.message);
}
}
},
}
</script>
<style scoped>
/* 新增库位统计卡片样式 */
.stat-card {
text-align: center;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.stat-card.occupied {
background-color: #fef0f0;
border: 1px solid #fbc4c4;
}
.stat-card.free {
background-color: #f0f9ee;
border: 1px solid #c4eac4;
}
.stat-card.total {
background-color: #eff4ff;
border: 1px solid #c4ccf0;
}
.stat-value {
font-size: 32px;
font-weight: bold;
margin-bottom: 10px;
}
.stat-label {
font-size: 16px;
color: #666;
}
.el-pagination {
padding-left: 5px;
}
.el-row .el-button {
width: 72px;
margin-left: 0px;
margin-right: 5px;
}
.table-class {
width: 100%;
}
.el-row .el-form-item .el-select-v2 {
width: 100% !important;
}
.el-row .el-form-item .el-input-number {
width: 100% !important;
}
</style>