chore: 保存盘点开发前改动

This commit is contained in:
zgw
2026-06-25 17:42:39 +08:00
parent 1af5b3cbd3
commit 4b8bc03343
114 changed files with 10005 additions and 1392 deletions

View File

@@ -0,0 +1,472 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="88px">
<el-form-item label="仓库名称" prop="warehouseId">
<el-select v-model="queryParams.warehouseId" placeholder="请选择仓库" clearable filterable>
<el-option
v-for="warehouse in warehouseOptions"
:key="warehouse.warehouseId"
:label="warehouse.warehouseName"
:value="warehouse.warehouseId"
/>
</el-select>
</el-form-item>
<el-form-item label="货位编码" prop="cargoSpaceId">
<el-input
v-model="queryParams.cargoSpaceId"
placeholder="请输入货位编码"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="货位名称" prop="cargoSpaceName">
<el-input
v-model="queryParams.cargoSpaceName"
placeholder="请输入货位名称"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="货位状态" prop="cargoSpaceStatus">
<el-select v-model="queryParams.cargoSpaceStatus" placeholder="请选择货位状态" clearable>
<el-option
v-for="dict in dict.type.bas_cargo_space_status"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="托盘条码" prop="trayCode">
<el-input
v-model="queryParams.trayCode"
placeholder="请输入托盘条码"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="物料编码" prop="productId">
<el-input
v-model="queryParams.productId"
placeholder="请输入物料编码"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="物料名称" prop="productName">
<el-input
v-model="queryParams.productName"
placeholder="请输入物料名称"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="批次号" prop="lotNo">
<el-input
v-model="queryParams.lotNo"
placeholder="请输入批次号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="货位范围" prop="showEmpty">
<el-switch v-model="queryParams.showEmpty" active-text="所有货位" inactive-text="有库存" />
</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-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-position"
size="mini"
:disabled="multiple"
@click="openBatchGenerateDialog"
v-hasPermi="['out:locationOutbound:generate']"
>批量生成作业</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table
v-loading="loading"
:data="locationList"
border
stripe
highlight-current-row
row-key="cargoSpaceId"
@selection-change="handleSelectionChange"
@row-click="handleRowClick"
>
<el-table-column type="selection" width="55" align="center" :selectable="canGenerate" />
<el-table-column label="货位编码" align="center" prop="cargoSpaceId" width="150" show-overflow-tooltip />
<el-table-column label="货位名称" align="center" prop="cargoSpaceName" min-width="150" show-overflow-tooltip />
<el-table-column label="货位状态" align="center" prop="cargoSpaceStatus" width="110">
<template slot-scope="scope">
<dict-tag :options="dict.type.bas_cargo_space_status" :value="scope.row.cargoSpaceStatus" />
</template>
</el-table-column>
<el-table-column label="仓库" align="center" prop="warehouseName" width="120" show-overflow-tooltip />
<el-table-column label="巷道" align="center" prop="roadwayId" width="80" />
<el-table-column label="排" align="center" prop="thePlatoon" width="70" />
<el-table-column label="列" align="center" prop="theColumn" width="70" />
<el-table-column label="层" align="center" prop="theFloor" width="70" />
<el-table-column label="库存ID" align="center" prop="stockId" width="160" show-overflow-tooltip />
<el-table-column label="托盘条码" align="center" prop="trayCode" width="140" show-overflow-tooltip />
<el-table-column label="托盘类型" align="center" prop="trayType" width="90" />
<el-table-column label="明细数" align="center" prop="detailCount" width="80" />
<el-table-column label="总库存" align="center" prop="quantity" width="90" />
<el-table-column label="已分配" align="center" prop="outQuantity" width="90" />
<el-table-column label="可用库存" align="center" prop="availableQuantity" width="100" />
<el-table-column label="入库时间" align="center" prop="inDate" width="160">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.inDate, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="120" fixed="right">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-position"
:disabled="!canGenerate(scope.row)"
@click="openGenerateDialog(scope.row)"
v-hasPermi="['out:locationOutbound:generate']"
>生成作业</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<div class="detail-panel">
<div class="detail-header">
<span class="detail-title">库存明细</span>
<el-tag v-if="currentLocation" type="primary" size="small">
货位:{{ currentLocation.cargoSpaceName || currentLocation.cargoSpaceId }}
</el-tag>
<span v-else class="detail-hint">(请点击上方货位行查看明细)</span>
</div>
<el-table
:data="locationDetailList"
border
stripe
size="small"
style="width: 100%;"
:header-cell-style="{ background: '#f5f7fa', color: '#606266' }"
>
<el-table-column label="库存明细ID" align="center" prop="detailId" width="180" show-overflow-tooltip />
<el-table-column label="物料编码" align="center" prop="productId" width="130" show-overflow-tooltip />
<el-table-column label="物料名称" align="center" prop="productName" min-width="160" show-overflow-tooltip />
<el-table-column label="批次号" align="center" prop="lotNo" width="130" show-overflow-tooltip />
<el-table-column label="库存数量" align="center" prop="quantity" width="100" />
<el-table-column label="已分配" align="center" prop="outQuantity" width="100" />
<el-table-column label="可用库存" align="center" prop="availableQuantity" width="110" />
<el-table-column label="入库时间" align="center" prop="inDate" width="160">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.inDate, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<template slot="empty">
<div class="detail-empty">
{{ currentLocation ? (currentLocation.stockId ? '暂无库存明细' : '当前货位无库存') : '请先选择上方货位行' }}
</div>
</template>
</el-table>
</div>
<el-dialog
title="指定货位出库"
:visible.sync="generateDialogVisible"
width="520px"
append-to-body
>
<el-descriptions v-if="currentRow" :column="1" size="small" border class="mb12">
<el-descriptions-item label="货位">{{ currentRow.cargoSpaceName || currentRow.cargoSpaceId }}</el-descriptions-item>
<el-descriptions-item label="托盘">{{ currentRow.trayCode || '-' }}</el-descriptions-item>
<el-descriptions-item label="可用库存">{{ currentRow.availableQuantity || 0 }}</el-descriptions-item>
</el-descriptions>
<el-form label-width="90px" size="small">
<el-form-item label="出库口" required>
<el-select
v-model="selectedExitId"
placeholder="请选择出库口"
filterable
clearable
style="width: 100%;"
:loading="exitLoading"
>
<el-option
v-for="point in exitOptions"
:key="point.pointId"
:label="formatExitLabel(point)"
:value="point.pointId"
/>
</el-select>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="generateDialogVisible = false">取消</el-button>
<el-button type="primary" :loading="generating" @click="confirmGenerate">确认生成</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { listLocationOutbound, generateLocationOutbound } from '@/api/out/locationOutbound'
import { listStationPoint } from '@/api/bas/stationpoint'
import { listWarehouse } from '@/api/bas/warehouse'
export default {
name: 'LocationOutbound',
dicts: ['bas_cargo_space_status'],
data() {
return {
loading: true,
showSearch: true,
total: 0,
locationList: [],
currentLocation: null,
locationDetailList: [],
selectedRows: [],
multiple: true,
isBatchGenerate: false,
batchGenerating: false,
warehouseOptions: [],
queryParams: {
pageNum: 1,
pageSize: 10,
warehouseId: null,
warehouseName: null,
cargoSpaceId: null,
cargoSpaceName: null,
cargoSpaceStatus: null,
trayCode: null,
productId: null,
productName: null,
lotNo: null,
showEmpty: false
},
generateDialogVisible: false,
generating: false,
exitLoading: false,
currentRow: null,
selectedExitId: null,
exitOptions: []
}
},
created() {
this.loadWarehouseOptions()
this.getList()
},
methods: {
loadWarehouseOptions() {
listWarehouse({ pageNum: 1, pageSize: 1000 }).then(response => {
this.warehouseOptions = response.rows || []
}).catch(() => {
this.warehouseOptions = []
})
},
getList() {
this.loading = true
listLocationOutbound(this.queryParams).then(response => {
this.locationList = response.rows || []
this.total = response.total || 0
this.refreshCurrentLocation()
this.loading = false
}).catch(() => {
this.loading = false
})
},
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
resetQuery() {
this.resetForm('queryForm')
this.queryParams.showEmpty = false
this.resetDetailQuery()
this.handleQuery()
},
resetDetailQuery() {
this.currentLocation = null
this.locationDetailList = []
},
refreshCurrentLocation() {
if (!this.currentLocation) {
this.locationDetailList = []
return
}
const current = this.locationList.find(row => row.cargoSpaceId === this.currentLocation.cargoSpaceId)
if (!current) {
this.resetDetailQuery()
return
}
this.handleRowClick(current)
},
handleRowClick(row) {
this.currentLocation = row
this.locationDetailList = row && row.details ? row.details : []
},
handleSelectionChange(selection) {
this.selectedRows = selection
this.multiple = selection.length === 0
},
canGenerate(row) {
if (!row) {
return false
}
if (!row.stockId) {
return ['1', '6'].includes(String(row.cargoSpaceStatus))
}
const availableQuantity = Number(row.availableQuantity || 0)
return String(row.cargoSpaceStatus) === '2' && availableQuantity > 0
},
openGenerateDialog(row) {
if (!this.canGenerate(row)) {
this.$modal.msgWarning('当前货位不能生成出库作业')
return
}
this.isBatchGenerate = false
this.currentRow = row
this.selectedExitId = null
this.generateDialogVisible = true
this.loadExitOptions(row)
},
openBatchGenerateDialog() {
if (this.selectedRows.length === 0) {
this.$modal.msgWarning('请选择可生成作业的货位')
return
}
const invalidRow = this.selectedRows.find(row => !this.canGenerate(row))
if (invalidRow) {
this.$modal.msgWarning('选中货位包含无可用库存或已生成出库作业的记录')
return
}
const rows = this.selectedRows
const warehouseIds = Array.from(new Set(rows.map(row => row.warehouseId).filter(Boolean)))
if (warehouseIds.length > 1) {
this.$modal.msgWarning('批量生成作业请选择同一仓库的货位')
return
}
this.isBatchGenerate = true
this.currentRow = rows[0]
this.selectedExitId = null
this.generateDialogVisible = true
this.loadExitOptions(rows[0])
},
loadExitOptions(row) {
this.exitLoading = true
listStationPoint({
warehouseId: row.warehouseId,
status: '0'
}).then(response => {
const rows = response.rows || response.data || []
this.exitOptions = rows.filter(point => {
return ['2', '3'].includes(String(point.pointType)) &&
String(point.pointStatus || '0') === '0' &&
this.pointSupportsTrayType(point, row.trayType)
})
if (this.exitOptions.length === 1) {
this.selectedExitId = this.exitOptions[0].pointId
}
this.exitLoading = false
}).catch(() => {
this.exitOptions = []
this.exitLoading = false
})
},
pointSupportsTrayType(point, trayType) {
if (!trayType || !point.trayType) {
return true
}
return String(point.trayType).split(',').map(item => item.trim()).includes(String(trayType))
},
formatExitLabel(point) {
const typeName = String(point.pointType) === '3' ? '入出库' : '出库'
const trayType = point.trayType ? `,${point.trayType}类托盘` : ''
return `${point.pointName || point.pointId}(${point.pointId},${typeName}${trayType})`
},
confirmGenerate() {
if (!this.currentRow || !this.currentRow.cargoSpaceId) {
this.$modal.msgWarning('请选择要出库的货位')
return
}
if (!this.selectedExitId) {
this.$modal.msgWarning('请选择出库口')
return
}
const rows = this.isBatchGenerate ? this.selectedRows.filter(row => this.canGenerate(row)) : [this.currentRow]
const confirmText = this.isBatchGenerate
? `确认将选中的 ${rows.length} 个货位生成出库作业?`
: this.currentRow.stockId
? `确认将货位 ${this.currentRow.cargoSpaceName || this.currentRow.cargoSpaceId} 的整托库存生成出库作业?`
: `确认将空货位 ${this.currentRow.cargoSpaceName || this.currentRow.cargoSpaceId} 生成出库作业?`
this.$modal.confirm(confirmText).then(() => {
this.generating = true
return Promise.all(rows.map(row => generateLocationOutbound({
stockId: row.stockId,
cargoSpaceId: row.cargoSpaceId,
exitId: this.selectedExitId
})))
}).then(response => {
const first = Array.isArray(response) && response.length > 0 ? response[0] : response
const message = this.isBatchGenerate ? '批量生成成功' : (first.msg || '生成成功')
this.$modal.msgSuccess(message)
this.generateDialogVisible = false
this.generating = false
this.getList()
}).catch(() => {
this.generating = false
})
}
}
}
</script>
<style scoped>
.detail-panel {
margin-top: 20px;
}
.detail-header {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 10px;
}
.detail-title {
font-size: 14px;
font-weight: 600;
color: #303133;
}
.detail-hint {
margin-left: 4px;
color: #909399;
font-size: 12px;
}
.detail-empty {
padding: 18px 24px;
color: #909399;
background: #f8fafc;
}
.mb12 {
margin-bottom: 12px;
}
</style>