Commit 722a2e78 by wangchunyang

退库、统计相关功能,仓库、库位选择组件

parent 67fafb93
......@@ -77,3 +77,20 @@ export function Ship(data) {
data: data
})
}
// 导出库存列表
export function exportInventory(query) {
return request({
url: '/inventory/inventory/export',
method: 'post',
params: query
})
}
// 查询库存明细列表(根据物料标识及检索条件)
export function listInventoryDetail(query) {
return request({
url: '/inventory/inventory/detailList',
method: 'get',
params: query
})
}
\ No newline at end of file
import request from '@/utils/request'
// 查询出入库统计列表
export function listInboundOutboundStatistics(query) {
return request({
url: '/inventory/statistics/inboundOutbound',
method: 'get',
params: query
})
}
// 导出出入库统计数据
export function exportInboundOutboundStatistics(query) {
return request({
url: '/inventory/statistics/inboundOutbound/export',
method: 'post',
params: query
})
}
......@@ -43,3 +43,12 @@ export function delLocations(id) {
method: 'delete'
})
}
// 查询库位列表(无权限限制,用于选择器)
export function listLocationsForSelector(query) {
return request({
url: '/inventory/locations/listForSelector',
method: 'get',
params: query
})
}
\ No newline at end of file
import request from '@/utils/request'
// 查询退库单明细列表
export function listReturnOrderItems(query) {
return request({
url: '/inventory/return_order_items/list',
method: 'get',
params: query
})
}
// 查询退库单明细详细
export function getReturnOrderItem(id) {
return request({
url: '/inventory/return_order_items/' + id,
method: 'get'
})
}
// 新增退库单明细
export function addReturnOrderItem(data) {
return request({
url: '/inventory/return_order_items',
method: 'post',
data: data
})
}
// 修改退库单明细
export function updateReturnOrderItem(data) {
return request({
url: '/inventory/return_order_items',
method: 'put',
data: data
})
}
// 删除退库单明细
export function delReturnOrderItem(id) {
return request({
url: '/inventory/return_order_items/' + id,
method: 'delete'
})
}
// 导出退库单明细
export function exportReturnOrderItems(query) {
return request({
url: '/inventory/return_order_items/export',
method: 'post',
params: query
})
}
import request from '@/utils/request'
// 查询退库单列表
export function listReturnOrders(query) {
return request({
url: '/inventory/return_orders/list',
method: 'get',
params: query
})
}
// 查询退库单详细
export function getReturnOrder(id) {
return request({
url: '/inventory/return_orders/' + id,
method: 'get'
})
}
// 新增退库单
export function addReturnOrder(data) {
return request({
url: '/inventory/return_orders',
method: 'post',
data: data
})
}
// 修改退库单
export function updateReturnOrder(data) {
return request({
url: '/inventory/return_orders',
method: 'put',
data: data
})
}
// 删除退库单
export function delReturnOrder(id) {
return request({
url: '/inventory/return_orders/' + id,
method: 'delete'
})
}
// 确认退库单
export function confirmReturnOrder(id) {
return request({
url: '/inventory/return_orders/confirm/' + id,
method: 'post'
})
}
......@@ -42,3 +42,12 @@ export function delWarehouses(id) {
method: 'delete'
})
}
// 查询仓库列表(无权限限制,用于选择器)
export function listWarehousesForSelector(query) {
return request({
url: '/inventory/warehouses/listForSelector',
method: 'get',
params: query
})
}
\ No newline at end of file
<template>
<el-dialog
:visible.sync="dialogVisible"
width="1000px"
append-to-body
class="location-selector-dialog"
:close-on-click-modal="false"
>
<span slot="title">
选择库位
<small class="title-tip">双击表格行可快速选择</small>
</span>
<div class="location-selector-body">
<el-form
:model="queryParams"
ref="queryForm"
size="small"
:inline="true"
label-width="90px"
class="location-selector-form"
>
<!-- <el-form-item label="库位编码" prop="locationCode">
<el-input
v-model="queryParams.locationCode"
placeholder="请输入库位编码"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item> -->
<el-form-item label="库位名称" prop="locationName">
<el-input
v-model="queryParams.locationName"
placeholder="请输入库位名称"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<!-- <el-form-item label="仓库编码" prop="warehousesId">
<el-input
v-model="queryParams.warehousesId"
placeholder="请输入仓库编码"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item> -->
<!-- <el-form-item label="是否启用" prop="isEnabled">
<el-select
v-model="queryParams.isEnabled"
placeholder="请选择"
clearable
style="width: 180px"
>
<el-option label="是" value="1" />
<el-option label="否" value="0" />
</el-select>
</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
v-loading="loading"
:data="locationsList"
:max-height="380"
highlight-current-row
@current-change="handleCurrentChange"
@row-dblclick="handleRowDblClick"
style="flex: 1; min-height: 0;"
>
<el-table-column type="index" label="#" width="60" align="center" />
<!-- <el-table-column label="库位编码" prop="locationCode" width="150" /> -->
<el-table-column label="库位名称" prop="locationName" min-width="150" show-overflow-tooltip />
<el-table-column label="库位类型" prop="locationType" width="120" align="center">
<template slot-scope="scope">
<span v-if="scope.row.locationType === 1">货架</span>
<span v-else-if="scope.row.locationType === 2">地面</span>
<span v-else-if="scope.row.locationType === 3">货位</span>
<span v-else-if="scope.row.locationType === 4">专区</span>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="区域代码" prop="zoneCode" width="120" />
<!-- <el-table-column label="是否启用" prop="isEnabled" width="110" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.isEnabled === '1' || scope.row.isEnabled === 1 ? 'success' : 'info'" size="mini">
{{ scope.row.isEnabled === '1' || scope.row.isEnabled === 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>
<div slot="footer" class="dialog-footer">
<el-button @click="close">取 消</el-button>
<el-button type="primary" @click="confirmSelection">确 定</el-button>
</div>
</el-dialog>
</template>
<script>
import { listLocationsForSelector } from "@/api/inventory/locations"
export default {
name: "LocationSelector",
props: {
value: {
type: Boolean,
default: false
},
warehousesId: {
type: String,
default: null
}
},
data() {
return {
locationsList: [],
loading: false,
total: 0,
currentLocation: null,
queryParams: {
pageNum: 1,
pageSize: 10,
locationCode: null,
locationName: null,
warehousesId: null,
isEnabled: "1"
}
}
},
computed: {
dialogVisible: {
get() {
return this.value
},
set(val) {
this.$emit("input", val)
}
}
},
watch: {
dialogVisible(val) {
if (val) {
this.$nextTick(() => {
this.init()
})
} else {
this.resetState()
}
},
warehousesId(val) {
if (val) {
this.queryParams.warehousesId = val
}
}
},
methods: {
init() {
// 如果传入了仓库编码,则自动设置查询条件
if (this.warehousesId) {
this.queryParams.warehousesId = this.warehousesId
}
if (this.locationsList.length === 0) {
this.getList()
} else {
// 如果仓库编码变化,重新查询
this.getList()
}
},
resetState() {
this.currentLocation = null
},
getList() {
this.loading = true
listLocationsForSelector(this.queryParams)
.then(res => {
this.locationsList = res.rows || []
this.total = res.total || 0
})
.finally(() => {
this.loading = false
})
},
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
resetQuery() {
this.queryParams = {
pageNum: 1,
pageSize: 10,
locationCode: null,
locationName: null,
isEnabled: "1",
warehousesId: this.warehousesId || null
}
// 如果传入了仓库编码,确保设置到查询参数中
if (this.warehousesId) {
this.queryParams.warehousesId = this.warehousesId
}
this.getList()
},
handleCurrentChange(row) {
this.currentLocation = row
},
handleRowDblClick(row) {
this.currentLocation = row
this.confirmSelection()
},
confirmSelection() {
if (!this.currentLocation) {
this.$message.warning("请先选择一个库位")
return
}
const payload = {
locationCode: this.currentLocation.locationCode,
locationName: this.currentLocation.locationName,
locationId: this.currentLocation.id,
warehousesId: this.currentLocation.warehousesId
}
this.$emit("selected", payload)
this.dialogVisible = false
},
close() {
this.dialogVisible = false
},
refresh() {
this.getList()
}
}
}
</script>
<style scoped>
.location-selector-dialog /deep/ .el-dialog__body {
padding: 10px 20px 0;
min-height: 500px;
display: flex;
flex-direction: column;
}
.location-selector-body {
display: flex;
flex-direction: column;
height: 100%;
min-height: 0;
}
.location-selector-form {
margin-bottom: 10px;
}
.title-tip {
font-size: 12px;
color: #999;
margin-left: 8px;
}
</style>
<template>
<el-dialog
:visible.sync="dialogVisible"
width="900px"
append-to-body
class="owner-selector-dialog"
:close-on-click-modal="false"
>
<span slot="title">
选择货主
<small class="title-tip">双击表格行可快速选择</small>
</span>
<div class="owner-selector-body">
<el-form
:model="queryParams"
ref="queryForm"
size="small"
:inline="true"
label-width="90px"
class="owner-selector-form"
>
<el-form-item label="货主编码" prop="ownerCode">
<el-input
v-model="queryParams.ownerCode"
placeholder="请输入货主编码"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="货主名称" prop="ownerName">
<el-input
v-model="queryParams.ownerName"
placeholder="请输入货主名称"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="是否激活" prop="isActive">
<el-select
v-model="queryParams.isActive"
placeholder="请选择"
clearable
style="width: 180px"
>
<el-option label="是" value="1" />
<el-option label="否" value="0" />
</el-select>
</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
v-loading="loading"
:data="ownersList"
:max-height="380"
highlight-current-row
@current-change="handleCurrentChange"
@row-dblclick="handleRowDblClick"
style="flex: 1; min-height: 0;"
>
<el-table-column type="index" label="#" width="60" align="center" />
<el-table-column label="货主编码" prop="ownerCode" width="150" />
<el-table-column label="货主名称" prop="ownerName" min-width="220" show-overflow-tooltip />
<el-table-column label="联系人" prop="contactPerson" width="150" />
<el-table-column label="联系电话" prop="contactPhone" width="150" />
<el-table-column label="是否激活" prop="isActive" width="110" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.isActive === '1' || scope.row.isActive === 1 ? 'success' : 'info'" size="mini">
{{ scope.row.isActive === '1' || scope.row.isActive === 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>
<div slot="footer" class="dialog-footer">
<el-button @click="close">取 消</el-button>
<el-button type="primary" @click="confirmSelection">确 定</el-button>
</div>
</el-dialog>
</template>
<script>
import { listOwners } from "@/api/inventory/owners"
export default {
name: "OwnerSelector",
props: {
value: {
type: Boolean,
default: false
}
},
data() {
return {
ownersList: [],
loading: false,
total: 0,
currentOwner: null,
queryParams: {
pageNum: 1,
pageSize: 10,
ownerCode: null,
ownerName: null,
isActive: "1"
}
}
},
computed: {
dialogVisible: {
get() {
return this.value
},
set(val) {
this.$emit("input", val)
}
}
},
watch: {
dialogVisible(val) {
if (val) {
this.$nextTick(() => {
this.init()
})
} else {
this.resetState()
}
}
},
methods: {
init() {
if (this.ownersList.length === 0) {
this.getList()
}
},
resetState() {
this.currentOwner = null
},
getList() {
this.loading = true
listOwners(this.queryParams)
.then(res => {
this.ownersList = res.rows || []
this.total = res.total || 0
})
.finally(() => {
this.loading = false
})
},
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
resetQuery() {
this.queryParams = {
pageNum: 1,
pageSize: 10,
ownerCode: null,
ownerName: null,
isActive: "1"
}
this.getList()
},
handleCurrentChange(row) {
this.currentOwner = row
},
handleRowDblClick(row) {
this.currentOwner = row
this.confirmSelection()
},
confirmSelection() {
if (!this.currentOwner) {
this.$message.warning("请先选择一个货主")
return
}
const payload = {
ownerCode: this.currentOwner.ownerCode,
ownerName: this.currentOwner.ownerName,
ownerId: this.currentOwner.id
}
this.$emit("selected", payload)
this.dialogVisible = false
},
close() {
this.dialogVisible = false
},
refresh() {
this.getList()
}
}
}
</script>
<style scoped>
.owner-selector-dialog /deep/ .el-dialog__body {
padding: 10px 20px 0;
min-height: 500px;
display: flex;
flex-direction: column;
}
.owner-selector-body {
display: flex;
flex-direction: column;
height: 100%;
min-height: 0;
}
.owner-selector-form {
margin-bottom: 10px;
}
.title-tip {
font-size: 12px;
color: #999;
margin-left: 8px;
}
</style>
<template>
<el-dialog
:visible.sync="dialogVisible"
width="900px"
append-to-body
class="warehouse-selector-dialog"
:close-on-click-modal="false"
>
<span slot="title">
选择仓库
<small class="title-tip">双击表格行可快速选择</small>
</span>
<div class="warehouse-selector-body">
<el-form
:model="queryParams"
ref="queryForm"
size="small"
:inline="true"
label-width="90px"
class="warehouse-selector-form"
>
<!-- <el-form-item label="仓库编码" prop="warehousesCode">
<el-input
v-model="queryParams.warehousesCode"
placeholder="请输入仓库编码"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item> -->
<el-form-item label="仓库名称" prop="warehousesName">
<el-input
v-model="queryParams.warehousesName"
placeholder="请输入仓库名称"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<!-- <el-form-item label="是否启用" prop="isEnabled">
<el-select
v-model="queryParams.isEnabled"
placeholder="请选择"
clearable
style="width: 180px"
>
<el-option label="是" value="1" />
<el-option label="否" value="0" />
</el-select>
</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
v-loading="loading"
:data="warehousesList"
:max-height="380"
highlight-current-row
@current-change="handleCurrentChange"
@row-dblclick="handleRowDblClick"
style="flex: 1; min-height: 0;"
>
<el-table-column type="index" label="#" width="60" align="center" />
<el-table-column label="仓库编码" prop="warehousesCode" width="150" />
<el-table-column label="仓库名称" prop="warehousesName" min-width="220" show-overflow-tooltip />
<el-table-column label="仓库类型" prop="warehouseType" width="120" align="center">
<template slot-scope="scope">
<span v-if="scope.row.warehouseType === 1">普通仓</span>
<span v-else-if="scope.row.warehouseType === 2">危险品仓</span>
<span v-else-if="scope.row.warehouseType === 3">冷藏仓</span>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="地址" prop="address" min-width="200" show-overflow-tooltip />
<el-table-column label="是否启用" prop="isEnabled" width="110" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.isEnabled === '1' || scope.row.isEnabled === 1 ? 'success' : 'info'" size="mini">
{{ scope.row.isEnabled === '1' || scope.row.isEnabled === 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>
<div slot="footer" class="dialog-footer">
<el-button @click="close">取 消</el-button>
<el-button type="primary" @click="confirmSelection">确 定</el-button>
</div>
</el-dialog>
</template>
<script>
import { listWarehousesForSelector } from "@/api/inventory/warehouses"
export default {
name: "WarehouseSelector",
props: {
value: {
type: Boolean,
default: false
}
},
data() {
return {
warehousesList: [],
loading: false,
total: 0,
currentWarehouse: null,
queryParams: {
pageNum: 1,
pageSize: 10,
warehousesCode: null,
warehousesName: null,
isEnabled: "1"
}
}
},
computed: {
dialogVisible: {
get() {
return this.value
},
set(val) {
this.$emit("input", val)
}
}
},
watch: {
dialogVisible(val) {
if (val) {
this.$nextTick(() => {
this.init()
})
} else {
this.resetState()
}
}
},
methods: {
init() {
if (this.warehousesList.length === 0) {
this.getList()
}
},
resetState() {
this.currentWarehouse = null
},
getList() {
this.loading = true
listWarehousesForSelector(this.queryParams)
.then(res => {
this.warehousesList = res.rows || []
this.total = res.total || 0
})
.finally(() => {
this.loading = false
})
},
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
resetQuery() {
this.queryParams = {
pageNum: 1,
pageSize: 10,
warehousesCode: null,
warehousesName: null,
isEnabled: "1"
}
this.getList()
},
handleCurrentChange(row) {
this.currentWarehouse = row
},
handleRowDblClick(row) {
this.currentWarehouse = row
this.confirmSelection()
},
confirmSelection() {
if (!this.currentWarehouse) {
this.$message.warning("请先选择一个仓库")
return
}
const payload = {
warehousesCode: this.currentWarehouse.warehousesCode,
warehousesName: this.currentWarehouse.warehousesName,
warehouseId: this.currentWarehouse.id
}
this.$emit("selected", payload)
this.dialogVisible = false
},
close() {
this.dialogVisible = false
},
refresh() {
this.getList()
}
}
}
</script>
<style scoped>
.warehouse-selector-dialog /deep/ .el-dialog__body {
padding: 10px 20px 0;
min-height: 500px;
display: flex;
flex-direction: column;
}
.warehouse-selector-body {
display: flex;
flex-direction: column;
height: 100%;
min-height: 0;
}
.warehouse-selector-form {
margin-bottom: 10px;
}
.title-tip {
font-size: 12px;
color: #999;
margin-left: 8px;
}
</style>
<template>
<div class="app-container">
<!-- 标题栏 + 操作按钮 -->
<PageTitle>
<template #buttons>
<el-button
type="warning"
plain
icon="el-icon-download"
size="medium"
@click="handleExport"
v-hasPermi="['inventory:statistics:export']"
>导出Excel</el-button>
</template>
</PageTitle>
<!-- 页面容器 -->
<div class="page-container">
<!-- 查询表单 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px">
<el-form-item label="时间段" prop="dateRange">
<el-date-picker
v-model="queryParams.dateRange"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="yyyy-MM-dd"
clearable
/>
</el-form-item>
<el-form-item label="仓库" prop="warehouseId">
<el-input
v-model="queryWarehouseName"
placeholder="请选择仓库"
readonly
@focus="openWarehouseSelector"
:suffix-icon="''"
>
<template v-if="queryWarehouseName" #suffix>
<i
class="el-icon-circle-close el-input__icon"
style="cursor: pointer;"
@click.stop="clearQueryWarehouse"
></i>
</template>
</el-input>
</el-form-item>
<el-form-item label="库位" prop="locationId">
<el-input
v-model="queryLocationName"
placeholder="请选择库位"
readonly
@focus="openLocationSelector"
:suffix-icon="''"
:disabled="!queryParams.warehousesCode"
>
<template v-if="queryLocationName" #suffix>
<i
class="el-icon-circle-close el-input__icon"
style="cursor: pointer;"
@click.stop="clearQueryLocation"
></i>
</template>
</el-input>
</el-form-item>
<el-form-item label="物料" prop="materialId">
<el-input
v-model="queryParams.materialId"
placeholder="请输入物料ID或编码"
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>
<!-- <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> -->
<!-- 表格区域 -->
<div class="table-container">
<el-table v-loading="loading" :data="statisticsList" border height="100%">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column label="物料ID" align="center" prop="materialId" width="150" />
<el-table-column label="物料名称" align="center" prop="materialName" min-width="150" show-overflow-tooltip />
<el-table-column label="入库次数" align="center" prop="inboundCount" width="100" />
<el-table-column label="出库次数" align="center" prop="outboundCount" width="100" />
<el-table-column label="入库数量" align="center" prop="inboundQuantity" width="120">
<template slot-scope="scope">
{{ formatNumber(scope.row.inboundQuantity) }}
</template>
</el-table-column>
<el-table-column label="出库数量" align="center" prop="outboundQuantity" width="120">
<template slot-scope="scope">
{{ formatNumber(scope.row.outboundQuantity) }}
</template>
</el-table-column>
<el-table-column label="入库总额" align="center" prop="inboundAmount" width="120">
<template slot-scope="scope">
{{ formatAmount(scope.row.inboundAmount) }}
</template>
</el-table-column>
<el-table-column label="出库总额" align="center" prop="outboundAmount" width="120">
<template slot-scope="scope">
{{ formatAmount(scope.row.outboundAmount) }}
</template>
</el-table-column>
<el-table-column label="总额差" align="center" prop="amountDiff" width="120">
<template slot-scope="scope">
<span :style="{ color: (scope.row.amountDiff || 0) >= 0 ? '#67C23A' : '#F56C6C' }">
{{ formatAmount(scope.row.amountDiff || ((scope.row.inboundAmount || 0) - (scope.row.outboundAmount || 0))) }}
</span>
</template>
</el-table-column>
</el-table>
</div>
<!-- 分页组件 -->
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
</div>
<!-- 仓库选择组件 -->
<WarehouseSelector
v-model="warehouseSelectorVisible"
@selected="handleWarehouseSelected"
/>
<!-- 库位选择组件 -->
<LocationSelector
v-model="locationSelectorVisible"
:warehouses-code="queryParams.warehousesCode"
@selected="handleLocationSelected"
/>
</div>
</template>
<script>
import { listInboundOutboundStatistics, exportInboundOutboundStatistics } from "@/api/inventory/inventory_statistics"
import RightToolbar from "@/components/RightToolbar"
import PageTitle from "@/components/PageTitle"
import WarehouseSelector from "@/views/compononents/WarehouseSelector.vue"
import LocationSelector from "@/views/compononents/LocationSelector.vue"
export default {
name: "InboundOutboundStatistics",
components: {
RightToolbar,
PageTitle,
WarehouseSelector,
LocationSelector
},
data() {
return {
// 遮罩层
loading: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 统计数据列表
statisticsList: [],
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
startDate: null,
endDate: null,
warehouseId: null,
warehousesCode: null,
locationId: null,
materialId: null,
dateRange: null
},
// 仓库选择相关
warehouseSelectorVisible: false,
queryWarehouseName: null,
// 库位选择相关
locationSelectorVisible: false,
queryLocationName: null
}
},
created() {
this.getList()
},
methods: {
/** 查询出入库统计列表 */
getList() {
this.loading = true
// 处理日期范围
const params = { ...this.queryParams }
if (params.dateRange && params.dateRange.length === 2) {
params.startDate = params.dateRange[0]
params.endDate = params.dateRange[1]
}
delete params.dateRange
listInboundOutboundStatistics(params).then(response => {
this.statisticsList = response.rows || []
this.total = response.total || 0
this.loading = false
}).catch(() => {
this.loading = false
})
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
/** 重置按钮操作 */
resetQuery() {
this.$refs.queryForm.resetFields()
this.queryWarehouseName = null
this.queryLocationName = null
this.queryParams.dateRange = null
this.queryParams.warehouseId = null
this.queryParams.warehousesCode = null
this.queryParams.locationId = null
this.handleQuery()
},
/** 打开仓库选择器 */
openWarehouseSelector() {
this.warehouseSelectorVisible = true
},
/** 仓库选择回调 */
handleWarehouseSelected(warehouse) {
if (!warehouse) return
this.queryParams.warehouseId = warehouse.warehouseId || warehouse.id
this.queryParams.warehousesCode = warehouse.warehousesCode
this.queryWarehouseName = warehouse.warehousesName || warehouse.warehousesCode
// 仓库选择后,清空库位信息
this.queryLocationName = null
this.queryParams.locationId = null
this.handleQuery()
},
/** 清空仓库选择 */
clearQueryWarehouse() {
this.queryWarehouseName = null
this.queryParams.warehouseId = null
this.queryParams.warehousesCode = null
// 清空仓库时,同时清空库位
this.queryLocationName = null
this.queryParams.locationId = null
this.handleQuery()
},
/** 打开库位选择器 */
openLocationSelector() {
if (!this.queryParams.warehousesCode) {
this.$message.warning("请先选择仓库")
return
}
this.locationSelectorVisible = true
},
/** 库位选择回调 */
handleLocationSelected(location) {
if (!location) return
this.queryParams.locationId = location.locationId || location.id
this.queryLocationName = location.locationName || location.locationCode
this.handleQuery()
},
/** 清空库位选择 */
clearQueryLocation() {
this.queryLocationName = null
this.queryParams.locationId = null
this.handleQuery()
},
/** 格式化金额,保留2位小数 */
formatAmount(amount) {
if (amount === null || amount === undefined || isNaN(amount)) {
return '0.00'
}
return parseFloat(amount).toFixed(2)
},
/** 格式化数量 */
formatNumber(num) {
if (num === null || num === undefined || isNaN(num)) {
return '0'
}
return parseFloat(num).toString()
},
/** 导出统计数据 */
handleExport() {
const params = { ...this.queryParams }
if (params.dateRange && params.dateRange.length === 2) {
params.startDate = params.dateRange[0]
params.endDate = params.dateRange[1]
}
delete params.dateRange
delete params.pageNum
delete params.pageSize
this.download('inventory/statistics/inboundOutbound/export', {
...params
}, `出入库统计数据_${new Date().getTime()}.xlsx`)
}
}
}
</script>
<style scoped>
/* 页面容器样式 */
.page-container {
padding: 16px;
background: #fff;
border-radius: 4px;
margin-bottom: 16px;
display: flex;
flex-direction: column;
height: calc(100vh - 140px);
min-height: 600px;
}
/* 表格容器样式 */
.table-container {
flex: 1;
min-height: 0;
margin-top: 16px;
display: flex;
flex-direction: column;
}
.mb8 {
margin-bottom: 8px;
}
</style>
<template>
<div class="app-container">
<!-- 标题栏 + 操作按钮 -->
<PageTitle>
<template #buttons>
<el-button
type="warning"
plain
icon="el-icon-download"
size="medium"
@click="handleExport"
v-hasPermi="['inventory:inventory:export']"
>导出Excel</el-button>
</template>
</PageTitle>
<!-- 页面容器 -->
<div class="page-container">
<!-- 查询表单 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px">
<el-form-item label="仓库" prop="warehousesCode">
<el-input
v-model="queryWarehouseName"
placeholder="请选择仓库"
readonly
@focus="openWarehouseSelector"
:suffix-icon="''"
>
<template v-if="queryWarehouseName" #suffix>
<i
class="el-icon-circle-close el-input__icon"
style="cursor: pointer;"
@click.stop="clearQueryWarehouse"
></i>
</template>
</el-input>
</el-form-item>
<el-form-item label="库位" prop="locationId">
<el-input
v-model="queryLocationName"
placeholder="请选择库位"
readonly
@focus="openLocationSelector"
:suffix-icon="''"
:disabled="!queryParams.warehousesCode"
>
<template v-if="queryLocationName" #suffix>
<i
class="el-icon-circle-close el-input__icon"
style="cursor: pointer;"
@click.stop="clearQueryLocation"
></i>
</template>
</el-input>
</el-form-item>
<el-form-item label="物料" prop="materialId">
<el-input
v-model="queryParams.materialId"
placeholder="请输入物料ID或编码"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="货主" prop="ownerId">
<el-input
v-model="queryOwnerName"
placeholder="请选择货主"
readonly
@focus="openOwnerSelector"
:suffix-icon="''"
>
<template v-if="queryOwnerName" #suffix>
<i
class="el-icon-circle-close el-input__icon"
style="cursor: pointer;"
@click.stop="clearQueryOwner"
></i>
</template>
</el-input>
</el-form-item>
<el-form-item label="库存类别" prop="inventoryType">
<el-select v-model="queryParams.inventoryType" placeholder="请选择库存类别" clearable>
<el-option
v-for="item in inventoryTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="入库批次号" prop="batchId">
<el-input
v-model="queryParams.batchId"
placeholder="请输入入库批次号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="预警" prop="alertType">
<el-select v-model="queryParams.alertType" placeholder="请选择预警类型" clearable>
<el-option
v-for="item in alertTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="库存状态" prop="inventoryStatus">
<el-select v-model="queryParams.inventoryStatus" placeholder="请选择库存状态" clearable>
<el-option
v-for="item in inventoryStatusOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</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>
<!-- <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> -->
<!-- 表格区域 -->
<div class="table-container">
<el-table v-loading="loading" :data="inventoryList" border height="100%">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column label="库存编号" align="center" prop="id" width="150" />
<el-table-column label="库存类别" align="center" prop="inventoryType" width="100">
<template slot-scope="scope">
<el-tag :type="scope.row.inventoryType === 1 ? 'primary' : 'warning'" size="small">
{{ getInventoryTypeName(scope.row.inventoryType) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="入库单号" align="center" prop="orderId" width="150" />
<el-table-column label="物料ID" align="center" prop="materialId" width="150" />
<el-table-column label="批次ID" align="center" prop="batchId" width="120" />
<el-table-column label="仓库编码" align="center" prop="warehousesCode" width="120" />
<el-table-column label="库位ID" align="center" prop="locationId" width="120" />
<el-table-column label="货主ID" align="center" prop="ownerId" width="120" />
<el-table-column label="库存数量" align="center" prop="quantity" width="100" />
<el-table-column label="锁定数量" align="center" prop="lockedQuantity" width="100" />
<el-table-column label="可用数量" align="center" width="100">
<template slot-scope="scope">
{{ (scope.row.quantity || 0) - (scope.row.lockedQuantity || 0) }}
</template>
</el-table-column>
<el-table-column label="单位重量" align="center" prop="unitWeight" width="100">
<template slot-scope="scope">
{{ formatWeight(scope.row.unitWeight) }}
</template>
</el-table-column>
<el-table-column label="总重量" align="center" prop="totalWeight" width="100">
<template slot-scope="scope">
{{ formatWeight(scope.row.totalWeight) }}
</template>
</el-table-column>
<el-table-column label="总体积" align="center" prop="totalVolume" width="100">
<template slot-scope="scope">
{{ formatVolume(scope.row.totalVolume) }}
</template>
</el-table-column>
<el-table-column label="生产日期" align="center" prop="productionDate" width="120">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.productionDate, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="失效日期" align="center" prop="expirationDate" width="120">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.expirationDate, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="库存状态" align="center" prop="inventoryStatus" width="100">
<template slot-scope="scope">
<el-tag
:type="getStatusType(scope.row.inventoryStatus)"
size="small"
>
{{ getStatusName(scope.row.inventoryStatus) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="最后入库时间" align="center" prop="lastInboundTime" width="160">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.lastInboundTime, '{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
<el-table-column label="最后出库时间" align="center" prop="lastOutboundTime" width="160">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.lastOutboundTime, '{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="160">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="100" fixed="right">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-view"
@click="handleViewDetail(scope.row)"
>明细</el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- 分页组件 -->
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
</div>
<!-- 货主选择组件 -->
<OwnerSelector
v-model="ownerSelectorVisible"
@selected="handleOwnerSelected"
/>
<!-- 仓库选择组件 -->
<WarehouseSelector
v-model="warehouseSelectorVisible"
@selected="handleWarehouseSelected"
/>
<!-- 库位选择组件 -->
<LocationSelector
v-model="locationSelectorVisible"
:warehouses-code="queryParams.warehousesCode"
@selected="handleLocationSelected"
/>
<!-- 物料明细弹窗 -->
<el-dialog
title="物料库存明细"
:visible.sync="detailDialogVisible"
width="1400px"
append-to-body
:close-on-click-modal="false"
>
<el-table
v-loading="detailLoading"
:data="detailList"
border
style="width: 100%"
max-height="600"
>
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column label="库存编号" align="center" prop="id" width="150" />
<el-table-column label="库存类别" align="center" prop="inventoryType" width="100">
<template slot-scope="scope">
<el-tag :type="scope.row.inventoryType === 1 ? 'primary' : 'warning'" size="small">
{{ getInventoryTypeName(scope.row.inventoryType) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="入库单号" align="center" prop="orderId" width="150" />
<el-table-column label="物料ID" align="center" prop="materialId" width="150" />
<el-table-column label="批次ID" align="center" prop="batchId" width="120" />
<el-table-column label="仓库编码" align="center" prop="warehousesCode" width="120" />
<el-table-column label="库位ID" align="center" prop="locationId" width="120" />
<el-table-column label="货主ID" align="center" prop="ownerId" width="120" />
<el-table-column label="库存数量" align="center" prop="quantity" width="100" />
<el-table-column label="锁定数量" align="center" prop="lockedQuantity" width="100" />
<el-table-column label="可用数量" align="center" width="100">
<template slot-scope="scope">
{{ (scope.row.quantity || 0) - (scope.row.lockedQuantity || 0) }}
</template>
</el-table-column>
<el-table-column label="单位重量" align="center" prop="unitWeight" width="100">
<template slot-scope="scope">
{{ formatWeight(scope.row.unitWeight) }}
</template>
</el-table-column>
<el-table-column label="总重量" align="center" prop="totalWeight" width="100">
<template slot-scope="scope">
{{ formatWeight(scope.row.totalWeight) }}
</template>
</el-table-column>
<el-table-column label="总体积" align="center" prop="totalVolume" width="100">
<template slot-scope="scope">
{{ formatVolume(scope.row.totalVolume) }}
</template>
</el-table-column>
<el-table-column label="生产日期" align="center" prop="productionDate" width="120">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.productionDate, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="失效日期" align="center" prop="expirationDate" width="120">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.expirationDate, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="库存状态" align="center" prop="inventoryStatus" width="100">
<template slot-scope="scope">
<el-tag
:type="getStatusType(scope.row.inventoryStatus)"
size="small"
>
{{ getStatusName(scope.row.inventoryStatus) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="最后入库时间" align="center" prop="lastInboundTime" width="160">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.lastInboundTime, '{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
<el-table-column label="最后出库时间" align="center" prop="lastOutboundTime" width="160">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.lastOutboundTime, '{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="160">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
<el-table-column label="创建人代码" align="center" prop="createUserCode" width="120" />
<el-table-column label="更新时间" align="center" prop="updateTime" width="160">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.updateTime, '{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
<el-table-column label="更新人代码" align="center" prop="updateUserCode" width="120" />
</el-table>
<div slot="footer" class="dialog-footer">
<el-button @click="detailDialogVisible = false">关闭</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { listInventory, exportInventory } from "@/api/inventory/inventory"
import RightToolbar from "@/components/RightToolbar"
import PageTitle from "@/components/PageTitle"
import OwnerSelector from "@/views/compononents/OwnerSelector.vue"
import WarehouseSelector from "@/views/compononents/WarehouseSelector.vue"
import LocationSelector from "@/views/compononents/LocationSelector.vue"
export default {
name: "InventoryDetail",
components: {
RightToolbar,
PageTitle,
OwnerSelector,
WarehouseSelector,
LocationSelector
},
data() {
return {
// 遮罩层
loading: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 库存明细表格数据
inventoryList: [],
// 库存类别选项(1普通2退库)
inventoryTypeOptions: [
{ value: 1, label: '普通' },
{ value: 2, label: '退库' }
],
// 预警类型选项
alertTypeOptions: [
{ value: 'all', label: '全部预警' },
{ value: 'over', label: '超量预警' },
{ value: 'under', label: '不足预警' }
],
// 库存状态选项(0-已出库 1-正常)
inventoryStatusOptions: [
{ value: 0, label: '已出库' },
{ value: 1, label: '正常' }
],
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
warehousesCode: null,
locationId: null,
materialId: null,
ownerId: null,
inventoryType: null,
batchId: null,
alertType: null,
inventoryStatus: null
},
// 货主选择相关
ownerSelectorVisible: false,
queryOwnerName: null,
// 仓库选择相关
warehouseSelectorVisible: false,
queryWarehouseName: null,
// 库位选择相关
locationSelectorVisible: false,
queryLocationName: null,
// 明细弹窗相关
detailDialogVisible: false,
detailLoading: false,
detailList: [],
currentDetailRow: null
}
},
created() {
this.getList()
},
methods: {
/** 查询库存明细列表 */
getList() {
this.loading = true
listInventory(this.queryParams).then(response => {
this.inventoryList = response.rows || []
this.total = response.total || 0
this.loading = false
}).catch(() => {
this.loading = false
})
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
/** 重置按钮操作 */
resetQuery() {
this.$refs.queryForm.resetFields()
this.queryOwnerName = null
this.queryWarehouseName = null
this.queryLocationName = null
this.queryParams.ownerId = null
this.queryParams.warehousesCode = null
this.queryParams.locationId = null
this.handleQuery()
},
/** 打开货主选择器 */
openOwnerSelector() {
this.ownerSelectorVisible = true
},
/** 货主选择回调 */
handleOwnerSelected(owner) {
if (!owner) return
this.queryParams.ownerId = owner.ownerId || owner.ownerCode
this.queryOwnerName = owner.ownerName || owner.ownerCode
this.handleQuery()
},
/** 清空货主选择 */
clearQueryOwner() {
this.queryOwnerName = null
this.queryParams.ownerId = null
this.handleQuery()
},
/** 打开仓库选择器 */
openWarehouseSelector() {
this.warehouseSelectorVisible = true
},
/** 仓库选择回调 */
handleWarehouseSelected(warehouse) {
if (!warehouse) return
this.queryParams.warehousesCode = warehouse.warehousesCode
this.queryWarehouseName = warehouse.warehousesName || warehouse.warehousesCode
// 仓库选择后,清空库位信息
this.queryLocationName = null
this.queryParams.locationId = null
this.handleQuery()
},
/** 打开库位选择器 */
openLocationSelector() {
if (!this.queryParams.warehousesCode) {
this.$message.warning("请先选择仓库")
return
}
this.locationSelectorVisible = true
},
/** 库位选择回调 */
handleLocationSelected(location) {
if (!location) return
this.queryParams.locationId = location.locationId || location.id
this.queryLocationName = location.locationName || location.locationCode
this.handleQuery()
},
/** 清空仓库选择 */
clearQueryWarehouse() {
this.queryWarehouseName = null
this.queryParams.warehousesCode = null
// 清空仓库时,同时清空库位
this.queryLocationName = null
this.queryParams.locationId = null
this.handleQuery()
},
/** 清空库位选择 */
clearQueryLocation() {
this.queryLocationName = null
this.queryParams.locationId = null
this.handleQuery()
},
/** 获取库存类别名称 */
getInventoryTypeName(type) {
const item = this.inventoryTypeOptions.find(item => item.value === type)
return item ? item.label : '未知'
},
/** 获取状态样式类型 */
getStatusType(status) {
const item = this.inventoryStatusOptions.find(item => item.value === status)
return item ? (status === 1 ? 'success' : 'info') : 'info'
},
/** 获取状态名称 */
getStatusName(status) {
const item = this.inventoryStatusOptions.find(item => item.value === status)
return item ? item.label : '未知'
},
/** 格式化重量 */
formatWeight(weight) {
if (weight === null || weight === undefined || isNaN(weight)) {
return '-'
}
return parseFloat(weight).toFixed(2)
},
/** 格式化体积 */
formatVolume(volume) {
if (volume === null || volume === undefined || isNaN(volume)) {
return '-'
}
return parseFloat(volume).toFixed(2)
},
/** 查看明细 */
handleViewDetail(row) {
this.currentDetailRow = row
this.detailDialogVisible = true
this.getDetailList(row)
},
/** 查询物料明细列表 */
getDetailList(row) {
this.detailLoading = true
// 根据当前行的物料ID、仓库、库位、货主等信息查询明细
const params = {
materialId: row.materialId,
warehousesCode: row.warehousesCode,
locationId: row.locationId,
ownerId: row.ownerId,
batchId: row.batchId,
inventoryType: row.inventoryType,
inventoryStatus: row.inventoryStatus,
pageNum: 1,
pageSize: 10000 // 设置一个很大的值,不分页
}
listInventory(params).then(response => {
this.detailList = response.rows || []
this.detailLoading = false
}).catch(() => {
this.detailLoading = false
})
},
/** 导出库存明细数据 */
handleExport() {
const params = { ...this.queryParams }
delete params.pageNum
delete params.pageSize
this.download('inventory/inventory/export', {
...params
}, `库存明细数据_${new Date().getTime()}.xlsx`)
}
}
}
</script>
<style scoped>
/* 页面容器样式 */
.page-container {
padding: 16px;
background: #fff;
border-radius: 4px;
margin-bottom: 16px;
display: flex;
flex-direction: column;
height: calc(100vh - 140px);
min-height: 600px;
}
/* 表格容器样式 */
.table-container {
flex: 1;
min-height: 0;
margin-top: 16px;
display: flex;
flex-direction: column;
}
.mb8 {
margin-bottom: 8px;
}
</style>
<template>
<div class="app-container">
<!-- 标题栏 + 操作按钮 -->
<PageTitle>
<template #buttons>
<el-button
type="primary"
plain
icon="el-icon-plus"
size="medium"
@click="handleAdd"
v-hasPermi="['inventory:return_orders:add']"
>新增</el-button>
<el-button
type="success"
plain
icon="el-icon-edit"
size="medium"
:disabled="single"
@click="handleUpdate()"
v-hasPermi="['inventory:return_orders:edit']"
>修改</el-button>
<el-button
type="danger"
plain
icon="el-icon-delete"
size="medium"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['inventory:return_orders:remove']"
>删除</el-button>
<el-button
type="warning"
plain
icon="el-icon-download"
size="medium"
@click="handleExport"
v-hasPermi="['inventory:return_orders:export']"
>导出</el-button>
</template>
</PageTitle>
<!-- 页面容器 -->
<div class="page-container">
<!-- 查询表单 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px">
<el-form-item label="退库单号" prop="orderId">
<el-input
v-model="queryParams.orderId"
placeholder="请输入退库单号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="退库日期" prop="dateRange">
<el-date-picker
v-model="queryParams.dateRange"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="yyyy-MM-dd"
clearable
/>
</el-form-item>
<el-form-item label="退库类型" prop="returnType">
<el-select v-model="queryParams.returnType" placeholder="请选择退库类型" clearable>
<el-option
v-for="item in returnTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="货主" prop="ownerId">
<el-input
v-model="queryOwnerName"
placeholder="请选择货主"
readonly
@focus="openOwnerSelector('query')"
:suffix-icon="''"
>
<template v-if="queryOwnerName" #suffix>
<i
class="el-icon-circle-close el-input__icon"
style="cursor: pointer;"
@click.stop="clearQueryOwner"
></i>
</template>
</el-input>
</el-form-item>
<el-form-item label="退库原因" prop="returnReason">
<el-input
v-model="queryParams.returnReason"
placeholder="请输入退库原因"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="退库单状态" prop="orderStatus">
<el-select v-model="queryParams.orderStatus" placeholder="请选择退库单状态" clearable class="w20">
<el-option
v-for="item in orderStatusOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</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>
<!-- <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> -->
<!-- 表格区域 -->
<div class="table-container">
<el-table v-loading="loading" :data="returnOrderList" height="100%" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="退库单号" align="center" prop="orderId" width="150" />
<el-table-column label="退库日期" align="center" prop="returnDate" width="120">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.returnDate, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="退库类型" align="center" prop="returnType" width="120">
<template slot-scope="scope">
<el-tag type="info" size="small">
{{ getReturnTypeName(scope.row.returnType) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="货主" align="center" prop="ownerName" width="120" />
<!-- <el-table-column label="原出库单号" align="center" prop="originalOrderId" width="150" /> -->
<el-table-column label="退库原因" align="center" prop="returnReason" show-overflow-tooltip />
<el-table-column label="退库单状态" align="center" prop="orderStatus" width="100">
<template slot-scope="scope">
<el-tag
:type="getStatusType(scope.row.orderStatus)"
size="small"
>
{{ getStatusName(scope.row.orderStatus) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="计划量" align="center" prop="totalPlannedQuantity" width="100" />
<el-table-column label="实际量" align="center" prop="totalActualQuantity" width="100" />
<el-table-column label="创建时间" align="center" prop="createTime" width="120">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="150">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['inventory:return_orders:edit']"
v-show="!(scope.row.orderStatus === 2)"
>修改</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-hasPermi="['inventory:return_orders:remove']"
v-show="!(scope.row.orderStatus === 2)"
>删除</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-success"
@click="handleConfirm(scope.row)"
v-hasPermi="['inventory:return_orders:confirm']"
v-show="!(scope.row.orderStatus === 2)"
>确认退库</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-more"
@click="handleDetail(scope.row)"
v-hasPermi="['inventory:return_orders:view']"
v-show="(scope.row.orderStatus === 2)"
>详情</el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- 分页组件 -->
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
</div>
<!-- 添加或修改退库单对话框 -->
<el-dialog :title="title" :visible.sync="open" width="1200px" append-to-body class="scrollable-dialog">
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
<el-divider content-position="center">退库单基础信息</el-divider>
<el-row :gutter="24">
<el-col :span="12">
<el-form-item label="退库单号" prop="orderId">
<el-input v-model="form.orderId" placeholder="请输入退库单号" :readonly="isDetailMode || form.orderStatus === 2" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="退库日期" prop="returnDate">
<el-date-picker
v-model="form.returnDate"
type="datetime"
value-format="yyyy-MM-dd HH:mm:ss"
placeholder="请选择退库日期"
style="width: 100%"
:disabled="isDetailMode || form.orderStatus === 2"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<el-col :span="12">
<el-form-item label="退库类型" prop="returnType">
<el-select v-model="form.returnType" placeholder="请选择退库类型" clearable style="width: 100%" :disabled="isDetailMode || form.orderStatus === 2">
<el-option
v-for="item in returnTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="货主" prop="ownerId">
<el-input
v-model="form.ownerName"
placeholder="请选择货主"
readonly
@focus="!isDetailMode && form.orderStatus !== 2 && openOwnerSelector('form')"
@click="!isDetailMode && form.orderStatus !== 2 && openOwnerSelector('form')"
/>
</el-form-item>
</el-col>
</el-row>
<!-- <el-row :gutter="24">
<el-col :span="12">
<el-form-item label="原出库单号" prop="originalOrderId">
<el-input v-model="form.originalOrderId" placeholder="请输入原出库单号" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="订单状态" prop="orderStatus">
<el-select v-model="form.orderStatus" placeholder="请选择订单状态" clearable style="width: 100%">
<el-option
v-for="item in orderStatusOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
</el-row> -->
<el-form-item label="退库原因" prop="returnReason">
<el-input v-model="form.returnReason" type="textarea" :rows="3" placeholder="请输入退库原因" :readonly="isDetailMode || form.orderStatus === 2" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请输入备注" :readonly="isDetailMode || form.orderStatus === 2" />
</el-form-item>
<el-divider content-position="center">退库物料明细</el-divider>
<!-- 明细表操作按钮 -->
<el-row :gutter="10" class="mb8" v-if="!isDetailMode && form.orderStatus !== 2">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAddItem"
>添加明细</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="multipleItems"
@click="handleDeleteItems"
>删除明细</el-button>
</el-col>
</el-row>
<!-- 明细表格 -->
<el-table
:data="form.returnOrderItemsList"
border
@selection-change="handleItemsSelectionChange"
style="margin-top: 10px"
>
<el-table-column type="selection" width="55" align="center" v-if="!isDetailMode && form.orderStatus !== 2" />
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column label="货物" prop="materialName" width="150" align="center" />
<el-table-column label="批次号" prop="batchCode" min-width="160" align="center">
<template slot-scope="scope">
<el-input
v-if="!isDetailMode && form.orderStatus !== 2"
v-model="scope.row.batchCode"
size="small"
placeholder="请输入批次"
/>
<span v-else>{{ scope.row.batchCode || '-' }}</span>
</template>
</el-table-column>
<el-table-column label="仓库" prop="warehouseName" width="160" align="center">
<template slot-scope="scope">
<span v-if="isDetailMode || form.orderStatus === 2" class="text-content">
{{ scope.row.warehouseName || scope.row.warehouseId || '-' }}
</span>
<el-input
v-else
v-model="scope.row.warehouseName"
placeholder="请选择仓库"
readonly
size="small"
@focus="openWarehouseSelector(scope.row)"
@click="openWarehouseSelector(scope.row)"
:suffix-icon="''"
>
<template v-if="scope.row.warehouseName" #suffix>
<i
class="el-icon-circle-close el-input__icon"
style="cursor: pointer;"
@click.stop="clearWarehouse(scope.row)"
></i>
</template>
</el-input>
</template>
</el-table-column>
<el-table-column label="库位" prop="locationName" width="180" align="center">
<template slot-scope="scope">
<span v-if="isDetailMode || form.orderStatus === 2" class="text-content">
{{ scope.row.locationName || scope.row.locationId || '-' }}
</span>
<el-input
v-else
v-model="scope.row.locationName"
placeholder="请选择库位"
readonly
size="small"
@focus="openLocationSelector(scope.row)"
@click="openLocationSelector(scope.row)"
:suffix-icon="''"
:disabled="!scope.row.warehouseId"
>
<template v-if="scope.row.locationName" #suffix>
<i
class="el-icon-circle-close el-input__icon"
style="cursor: pointer;"
@click.stop="clearLocation(scope.row)"
></i>
</template>
</el-input>
</template>
</el-table-column>
<el-table-column label="计划数量" prop="plannedQuantity" width="140" align="center">
<template slot-scope="scope">
<el-input-number
v-if="!isDetailMode && form.orderStatus !== 2"
v-model="scope.row.plannedQuantity"
:min="0"
size="small"
style="width: 100%"
/>
<span v-else>{{ scope.row.plannedQuantity || 0 }}</span>
</template>
</el-table-column>
<el-table-column label="实际数量" prop="actualQuantity" width="140" align="center">
<template slot-scope="scope">
<el-input-number
v-if="!isDetailMode && form.orderStatus !== 2"
v-model="scope.row.actualQuantity"
:min="0"
size="small"
style="width: 100%"
@change="handleQuantityChange(scope.row)"
/>
<span v-else>{{ scope.row.actualQuantity || 0 }}</span>
</template>
</el-table-column>
<el-table-column label="单价" prop="unitPrice" width="140" align="center">
<template slot-scope="scope">
<el-input-number
v-if="!isDetailMode && form.orderStatus !== 2"
v-model="scope.row.unitPrice"
:min="0"
:precision="2"
size="small"
style="width: 100%"
@change="handleAmountChange(scope.row)"
/>
<span v-else>{{ formatAmount(scope.row.unitPrice || 0) }}</span>
</template>
</el-table-column>
<el-table-column label="金额" prop="totalAmount" width="120" align="center">
<template slot-scope="scope">
{{ formatAmount(scope.row.totalAmount || (scope.row.actualQuantity || 0) * (scope.row.unitPrice || 0)) }}
</template>
</el-table-column>
<el-table-column label="备注" prop="remark" min-width="200">
<template slot-scope="scope">
<el-input
v-if="!isDetailMode && form.orderStatus !== 2"
v-model="scope.row.remark"
size="small"
placeholder="请输入备注"
/>
<span v-else>{{ scope.row.remark || '-' }}</span>
</template>
</el-table-column>
</el-table>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button v-if="!isDetailMode" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
<!-- 物料选择弹窗 -->
<el-dialog
title="选择物料"
:visible.sync="materialSelectOpen"
width="1200px"
append-to-body
>
<MaterialSelector
v-model="selectedMaterialIds"
:multiple="true"
@change="handleMaterialChange"
ref="materialSelector"
/>
<div slot="footer">
<el-button @click="materialSelectOpen = false">取消</el-button>
<el-button type="primary" @click="confirmMaterialSelect">确定</el-button>
</div>
</el-dialog>
<OwnerSelector
v-model="ownerSelectorVisible"
@selected="handleOwnerSelected"
/>
<!-- 仓库选择组件 -->
<WarehouseSelector
v-model="warehouseSelectorVisible"
@selected="handleWarehouseSelected"
/>
<!-- 库位选择组件 -->
<LocationSelector
v-model="locationSelectorVisible"
:warehousesId="currentEditRowWarehousesId"
@selected="handleLocationSelected"
/>
</div>
</template>
<script>
import { listReturnOrders, getReturnOrder, delReturnOrder, addReturnOrder, updateReturnOrder, confirmReturnOrder } from "@/api/inventory/return_orders"
import RightToolbar from "@/components/RightToolbar"
import PageTitle from "@/components/PageTitle"
import MaterialSelector from "@/views/inventory/materials/materialsSeletor.vue"
import OwnerSelector from "@/views/compononents/OwnerSelector.vue"
import WarehouseSelector from "@/views/compononents/WarehouseSelector.vue"
import LocationSelector from "@/views/compononents/LocationSelector.vue"
export default {
name: "ReturnOp",
components: {
RightToolbar,
PageTitle,
MaterialSelector,
OwnerSelector,
WarehouseSelector,
LocationSelector
},
data() {
return {
// 退库类型选项(字典:1-TRDC退料 2-质量退料 3-多出退料 4-其他,默认1)
returnTypeOptions: [
{ value: '1', label: 'TRDC退料' },
{ value: '2', label: '质量退料' },
{ value: '3', label: '多出退料' },
{ value: '4', label: '其他' }
],
// 订单状态选项(字典:1-草稿 2-已完成)
orderStatusOptions: [
{ value: 1, label: '草稿' },
{ value: 2, label: '已完成' }
],
// 物料选择相关
materialSelectOpen: false,
selectedMaterialIds: [],
selectedMaterials: [],
ownerSelectorVisible: false,
ownerSelectTarget: 'form',
queryOwnerName: null,
// 仓库和库位选择相关
warehouseSelectorVisible: false,
locationSelectorVisible: false,
currentEditRow: null,
currentEditRowWarehousesId: null,
// 遮罩层
loading: true,
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 退库单表格数据
returnOrderList: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 是否为详情模式
isDetailMode: false,
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
orderId: null,
dateRange: null,//时间段
returnType: null,
ownerId: null,
ownerName: null,
ownerCode: null,
originalOrderId: null,
returnReason: null,
orderStatus: null
},
// 表单参数
form: {
id: null,
orderId: null,
returnDate: null,
returnType: '1',
ownerId: null,
ownerName: null,
originalOrderId: null,
returnReason: null,
orderStatus: 1,
totalPlannedQuantity: 0,
totalActualQuantity: 0,
remark: null,
returnOrderItemsList: []
},
// 表单校验
rules: {
orderId: [
{ required: true, message: "退库单号不能为空", trigger: "blur" }
],
returnDate: [
{ required: true, message: "退库日期不能为空", trigger: "change" }
],
returnType: [
{ required: true, message: "退库类型不能为空", trigger: "change" }
]
},
// 明细选中项
selectedItems: [],
multipleItems: true,
returnRows: [] //入库单选择
}
},
created() {
this.getList()
},
methods: {
/** 查询退库单列表 */
getList() {
this.loading = true
listReturnOrders(this.queryParams).then(response => {
this.returnOrderList = response.rows || []
this.total = response.total || 0
this.loading = false
}).catch(() => {
this.loading = false
})
},
/** 获取状态样式类型 */
getStatusType(status) {
const item = this.orderStatusOptions.find(item => item.value === status)
return item ? (status === 1 ? 'info' : 'success') : 'info'
},
/** 获取状态名称 */
getStatusName(status) {
const item = this.orderStatusOptions.find(item => item.value === status)
return item ? item.label : '未知'
},
/** 获取退库类型名称 */
getReturnTypeName(type) {
if (!type) return '未知类型'
const item = this.returnTypeOptions.find(item => item.value === type)
return item ? item.label : '未知类型'
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
/** 重置按钮操作 */
resetQuery() {
this.$refs.queryForm.resetFields()
this.queryOwnerName = null
this.handleQuery()
},
/** 多选框选中数据 */
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id)
this.single = selection.length !== 1
this.multiple = !selection.length
this.returnRows = selection
},
/** 新增按钮操作 */
handleAdd() {
this.reset()
this.isDetailMode = false
this.open = true
this.title = "新建退库单"
},
/** 修改按钮操作 */
handleUpdate(row) {
if(!row) {
if(this.returnRows.length === 1) {
row = this.returnRows[0]
}else{
this.$message.warning("请选择一个退库单进行修改!")
}
}
this.reset()
const id = row.id || this.ids[0]
getReturnOrder(id).then(response => {
this.form = response.data
// 如果后端返回的明细数据为空,初始化为空数组
if (!this.form.returnOrderItemsList) {
this.form.returnOrderItemsList = []
}
this.form.returnOrderItemsList = this.form.returnOrderItemsList.map(item => ({
...item,
ownerName: item.ownerName || item.ownerId,
warehouseName: item.warehouseName || item.warehouseId,
locationName: item.locationName || item.locationCode || item.locationId
}))
this.form.ownerName = response.data.ownerName || response.data.ownerCode || response.data.ownerId
this.isDetailMode = false
this.open = true
this.title = "修改退库单"
})
},
/** 详情按钮操作 */
handleDetail(row) {
if(!row) {
this.$message.warning("请选择一个退库单!")
return
}
this.reset()
this.isDetailMode = true
const id = row.id
getReturnOrder(id).then(response => {
this.form = response.data
// 如果后端返回的明细数据为空,初始化为空数组
if (!this.form.returnOrderItemsList) {
this.form.returnOrderItemsList = []
}
this.form.returnOrderItemsList = this.form.returnOrderItemsList.map(item => ({
...item,
ownerName: item.ownerName || item.ownerId,
warehouseName: item.warehouseName || item.warehouseId,
locationName: item.locationName || item.locationCode || item.locationId
}))
this.form.ownerName = response.data.ownerName || response.data.ownerCode || response.data.ownerId
this.open = true
this.title = "退库单详情"
})
},
/** 确认按钮操作 */
handleConfirm(row) {
const ids = row.id || this.ids
this.$confirm('是否确认退库的数据项?', '提示', {
type: 'warning'
}).then(() => {
return confirmReturnOrder(ids)
}).then(() => {
this.getList()
this.$message.success("确认退库成功")
}).catch(() => {})
},
openOwnerSelector(target = 'form') {
this.ownerSelectTarget = target
this.ownerSelectorVisible = true
},
handleOwnerSelected(owner) {
if (!owner) return
if (this.ownerSelectTarget === 'query') {
this.queryParams.ownerId = owner.ownerId || owner.ownerCode
this.queryOwnerName = owner.ownerName || owner.ownerCode
this.handleQuery()
} else {
this.form.ownerId = owner.ownerId || owner.ownerCode
this.form.ownerCode = owner.ownerCode
this.form.ownerName = owner.ownerName
}
},
/** 打开仓库选择器 */
openWarehouseSelector(row) {
if (this.isDetailMode || this.form.orderStatus === 2) return
this.currentEditRow = row
this.warehouseSelectorVisible = true
},
/** 仓库选择回调 */
handleWarehouseSelected(warehouse) {
if (!warehouse || !this.currentEditRow) return
this.currentEditRow.warehouseId = warehouse.warehouseId || warehouse.id
this.currentEditRow.warehouseId = warehouse.warehouseId
this.currentEditRow.warehouseName = warehouse.warehousesName || warehouse.warehouseId
// 仓库选择后,清空库位信息
this.currentEditRow.locationId = null
this.currentEditRow.locationName = null
this.currentEditRow.locationCode = null
this.currentEditRow = null
},
/** 打开库位选择器 */
openLocationSelector(row) {
if (this.isDetailMode || this.form.orderStatus === 2) return
if (!row.warehouseId) {
this.$message.warning("请先选择仓库")
return
}
this.currentEditRow = row
this.currentEditRowWarehousesId = row.warehouseId
this.locationSelectorVisible = true
},
/** 库位选择回调 */
handleLocationSelected(location) {
if (!location || !this.currentEditRow) return
this.currentEditRow.locationId = location.locationId || location.id
this.currentEditRow.locationName = location.locationName || location.locationCode
this.currentEditRow.locationCode = location.locationCode
this.currentEditRow = null
this.currentEditRowWarehousesId = null
},
/** 清空仓库选择 */
clearWarehouse(row) {
row.warehouseId = null
row.warehouseId = null
row.warehouseName = null
// 清空仓库时,同时清空库位
row.locationId = null
row.locationName = null
row.locationCode = null
},
/** 清空库位选择 */
clearLocation(row) {
row.locationId = null
row.locationName = null
row.locationCode = null
},
clearQueryOwner() {
this.queryOwnerName = null
this.queryParams.ownerId = null
this.handleQuery()
},
/** 删除按钮操作 */
handleDelete(row) {
const ids = row.id || this.ids
this.$confirm('是否确认删除退库单编号为"' + ids + '"的数据项?', '提示', {
type: 'warning'
}).then(() => {
return delReturnOrder(ids)
}).then(() => {
this.getList()
this.$message.success("删除成功")
}).catch(() => {})
},
/** 添加明细按钮 */
handleAddItem() {
this.materialSelectOpen = true
this.selectedMaterialIds = []
this.selectedMaterials = []
this.$nextTick(() => {
if (this.$refs.materialSelector) {
this.$refs.materialSelector.clearSelection()
}
})
},
/** 物料选择变化回调 */
handleMaterialChange(selectedRows) {
this.selectedMaterials = selectedRows
},
/** 确认选择物料并添加到明细 */
confirmMaterialSelect() {
if (this.selectedMaterials.length === 0) {
this.$message.warning("请选择物料")
return
}
// 将选中的物料添加到退库明细
this.selectedMaterials.forEach(material => {
// 检查是否已存在该物料,避免重复添加
const exists = this.form.returnOrderItemsList.some(
item => item.materialCode === material.sapNo
)
if (!exists) {
this.form.returnOrderItemsList.push({
materialId: material.id,
materialCode: material.sapNo,
materialName: material.materialName,
batchCode: null,
warehouseId: null,
warehouseName: null,
locationId: null,
locationName: null,
locationCode: null,
plannedQuantity: 0,
actualQuantity: 0,
unitPrice: 0,
totalAmount: 0,
remark: null
})
} else {
this.$message.warning(`物料 ${material.materialCode} 已存在,跳过添加`)
}
})
this.materialSelectOpen = false
this.$message.success(`成功添加 ${this.selectedMaterials.length} 个物料`)
},
/** 明细项选择变化 */
handleItemsSelectionChange(selection) {
this.selectedItems = selection
this.multipleItems = selection.length === 0
},
/** 删除明细项 */
handleDeleteItems() {
if (this.selectedItems.length === 0) {
this.$message.warning("请选择要删除的明细项")
return
}
this.$confirm('确认删除选中的 ' + this.selectedItems.length + ' 条明细吗?', '提示', {
type: 'warning'
}).then(() => {
const selectedIds = this.selectedItems.map(item => item.materialId)
this.form.returnOrderItemsList = this.form.returnOrderItemsList.filter(
item => !selectedIds.includes(item.materialId)
)
this.selectedItems = []
this.multipleItems = true
this.$message.success("删除成功")
}).catch(() => {})
},
/** 数量变化时计算金额 */
handleQuantityChange(row) {
// 重新计算金额
const amount = (row.actualQuantity || 0) * (row.unitPrice || 0)
row.totalAmount = parseFloat(amount.toFixed(2))
this.calculateTotals()
},
/** 金额变化 */
handleAmountChange(row) {
const amount = (row.actualQuantity || 0) * (row.unitPrice || 0)
row.totalAmount = parseFloat(amount.toFixed(2))
},
/** 格式化金额,保留2位小数 */
formatAmount(amount) {
if (amount === null || amount === undefined || isNaN(amount)) {
return '0.00'
}
return parseFloat(amount).toFixed(2)
},
/** 计算总数 */
calculateTotals() {
if (!this.form.returnOrderItemsList || this.form.returnOrderItemsList.length === 0) {
this.form.totalPlannedQuantity = 0
this.form.totalActualQuantity = 0
return
}
const totals = this.form.returnOrderItemsList.reduce(
(acc, item) => {
acc.plannedQuantity += parseFloat(item.plannedQuantity) || 0
acc.actualQuantity += parseFloat(item.actualQuantity) || 0
return acc
},
{ plannedQuantity: 0, actualQuantity: 0 }
)
this.form.totalPlannedQuantity = totals.plannedQuantity
this.form.totalActualQuantity = totals.actualQuantity
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (!valid) return
// 验证明细表
if (!this.form.returnOrderItemsList || this.form.returnOrderItemsList.length === 0) {
this.$message.warning('请至少添加一条退库明细')
return
}
// 自动计算总数
this.calculateTotals()
// 提交数据
const submitData = {
...this.form,
returnOrderItemsList: this.form.returnOrderItemsList.map(item => ({
...item,
orderId: this.form.orderId // 确保明细项有主表ID
}))
}
if (this.form.id != null) {
updateReturnOrder(submitData).then(response => {
this.$message.success("修改成功")
this.open = false
this.getList()
})
} else {
addReturnOrder(submitData).then(response => {
this.$message.success("新增成功")
this.open = false
this.getList()
})
}
})
},
/** 取消按钮 */
cancel() {
this.open = false
this.reset()
},
/** 表单重置 */
reset() {
this.form = {
id: null,
orderId: null,
returnDate: null,
returnType: '1',
ownerId: null,
ownerName: null,
ownerCode: null,
originalOrderId: null,
returnReason: null,
orderStatus: 1,
totalPlannedQuantity: 0,
totalActualQuantity: 0,
remark: null,
returnOrderItemsList: []
}
this.selectedItems = []
this.multipleItems = true
this.isDetailMode = false
if (this.$refs.form) {
this.$refs.form.resetFields()
}
},
/** 导出退库单数据 */
handleExport() {
const params = { ...this.queryParams }
if (params.dateRange && params.dateRange.length === 2) {
params.startDate = params.dateRange[0]
params.endDate = params.dateRange[1]
}
delete params.dateRange
delete params.pageNum
delete params.pageSize
this.download('inventory/return_order_items/export', {
...params
}, `退库物料数据_${new Date().getTime()}.xlsx`)
}
}
}
</script>
<style scoped>
/* 页面容器样式 */
.page-container {
padding: 16px;
background: #fff;
border-radius: 4px;
margin-bottom: 16px;
display: flex;
flex-direction: column;
height: calc(100vh - 140px);
min-height: 600px;
}
/* 表格容器样式 */
.table-container {
flex: 1;
min-height: 0;
margin-top: 16px;
display: flex;
flex-direction: column;
}
.scrollable-dialog .el-dialog__body {
max-height: 80vh;
overflow-y: auto;
padding: 20px;
}
.mb8 {
margin-bottom: 8px;
}
.link-text {
color: #1890ff;
cursor: pointer;
}
.link-text:hover {
text-decoration: underline;
}
.text-content {
color: #606266;
}
.w20 {
width: 120px;
}
</style>
<template>
<div class="app-container">
<!-- 标题栏 + 操作按钮 -->
<PageTitle>
<template #buttons>
<el-button
type="warning"
plain
icon="el-icon-download"
size="medium"
@click="handleExport"
v-hasPermi="['inventory:return_order_items:export']"
>导出Excel</el-button>
</template>
</PageTitle>
<!-- 页面容器 -->
<div class="page-container">
<!-- 物料查询表单 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px">
<el-form-item label="退库单号" prop="orderId">
<el-input
v-model="queryParams.orderId"
placeholder="请输入退库单号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="时间段" prop="dateRange">
<el-date-picker
v-model="queryParams.dateRange"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="yyyy-MM-dd"
clearable
/>
</el-form-item>
<el-form-item label="货主" prop="ownerId">
<el-input
v-model="queryOwnerName"
placeholder="请选择货主"
readonly
@focus="openOwnerSelector"
:suffix-icon="''"
>
<template v-if="queryOwnerName" #suffix>
<i
class="el-icon-circle-close el-input__icon"
style="cursor: pointer;"
@click.stop="clearQueryOwner"
></i>
</template>
</el-input>
</el-form-item>
<el-form-item label="物料" prop="materialId">
<el-input
v-model="queryParams.materialId"
placeholder="请输入物料ID或编码"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="仓库" prop="warehouseId">
<el-input
v-model="queryWarehouseName"
placeholder="请选择仓库"
readonly
@focus="openWarehouseSelector"
:suffix-icon="''"
>
<template v-if="queryWarehouseName" #suffix>
<i
class="el-icon-circle-close el-input__icon"
style="cursor: pointer;"
@click.stop="clearQueryWarehouse"
></i>
</template>
</el-input>
</el-form-item>
<el-form-item label="库位" prop="locationId">
<el-input
v-model="queryLocationName"
placeholder="请选择库位"
readonly
@focus="openLocationSelector"
:suffix-icon="''"
:disabled="!queryParams.warehousesCode"
>
<template v-if="queryLocationName" #suffix>
<i
class="el-icon-circle-close el-input__icon"
style="cursor: pointer;"
@click.stop="clearQueryLocation"
></i>
</template>
</el-input>
</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>
<!-- <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> -->
<!-- 物料查询表格 -->
<div class="table-container">
<el-table v-loading="loading" :data="returnOrderItemList" border height="100%" style="width: 100%">
<el-table-column type="index" label="序号" width="60" align="center" fixed="left" />
<el-table-column label="退库单号" align="center" prop="orderId" width="150" />
<el-table-column label="退库日期" align="center" prop="returnDate" width="120">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.returnDate, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="货主" align="center" prop="ownerName" width="120" />
<el-table-column label="物料ID" align="center" prop="materialId" width="150" />
<el-table-column label="物料名称" align="center" prop="materialName" min-width="150" show-overflow-tooltip />
<el-table-column label="批次ID" align="center" prop="batchCode" width="120" />
<el-table-column label="仓库" align="center" prop="warehouseName" width="120" />
<el-table-column label="库位" align="center" prop="locationName" width="120" />
<el-table-column label="计划数量" align="center" prop="plannedQuantity" width="100" />
<el-table-column label="实际数量" align="center" prop="actualQuantity" width="100" />
<el-table-column label="单价" align="center" prop="unitPrice" width="100">
<template slot-scope="scope">
{{ formatAmount(scope.row.unitPrice) }}
</template>
</el-table-column>
<el-table-column label="金额" align="center" prop="totalAmount" width="120">
<template slot-scope="scope">
{{ formatAmount(scope.row.totalAmount || (scope.row.actualQuantity || 0) * (scope.row.unitPrice || 0)) }}
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="remark" min-width="150" show-overflow-tooltip />
<el-table-column label="操作" align="center" width="100" fixed="right">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-view"
@click="handleViewDetail(scope.row)"
>详情</el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- 分页组件 -->
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
</div>
<!-- 货主选择组件 -->
<OwnerSelector
v-model="ownerSelectorVisible"
@selected="handleOwnerSelected"
/>
<!-- 仓库选择组件 -->
<WarehouseSelector
v-model="warehouseSelectorVisible"
@selected="handleWarehouseSelected"
/>
<!-- 库位选择组件 -->
<LocationSelector
v-model="locationSelectorVisible"
:warehousesId="queryParams.warehouseId"
@selected="handleLocationSelected"
/>
<!-- 详情弹窗中的仓库选择组件 -->
<WarehouseSelector
v-model="detailWarehouseSelectorVisible"
@selected="handleDetailWarehouseSelected"
/>
<!-- 详情弹窗中的库位选择组件 -->
<LocationSelector
v-model="detailLocationSelectorVisible"
:warehousesId="detailQueryParams.warehouseId"
@selected="handleDetailLocationSelected"
/>
<!-- 详情弹窗 -->
<el-dialog
title="退库物料详情"
:visible.sync="detailDialogVisible"
width="1400px"
append-to-body
:close-on-click-modal="false"
>
<el-form :model="detailQueryParams" ref="detailQueryForm" size="small" :inline="true" label-width="100px" style="margin-bottom: 15px;">
<el-form-item label="仓库" prop="warehouseId">
<el-input
v-model="detailWarehouseName"
placeholder="请选择仓库"
readonly
@focus="openDetailWarehouseSelector"
:suffix-icon="''"
style="width: 200px;"
>
<template v-if="detailWarehouseName" #suffix>
<i
class="el-icon-circle-close el-input__icon"
style="cursor: pointer;"
@click.stop="clearDetailWarehouse"
></i>
</template>
</el-input>
</el-form-item>
<el-form-item label="库位" prop="locationId">
<el-input
v-model="detailLocationName"
placeholder="请选择库位"
readonly
@focus="openDetailLocationSelector"
:suffix-icon="''"
:disabled="!detailQueryParams.warehousesCode"
style="width: 200px;"
>
<template v-if="detailLocationName" #suffix>
<i
class="el-icon-circle-close el-input__icon"
style="cursor: pointer;"
@click.stop="clearDetailLocation"
></i>
</template>
</el-input>
</el-form-item>
<el-form-item label="批次号" prop="batchCode">
<el-input
v-model="detailQueryParams.batchCode"
placeholder="请输入批次号"
clearable
@keyup.enter.native="handleDetailQuery"
style="width: 200px;"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleDetailQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetDetailQuery">重置</el-button>
</el-form-item>
</el-form>
<el-table
v-loading="detailLoading"
:data="detailList"
border
style="width: 100%"
>
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column label="退库单号" align="center" prop="orderId" width="150" />
<el-table-column label="物料ID" align="center" prop="materialId" width="150" />
<el-table-column label="物料名称" align="center" prop="materialName" min-width="150" show-overflow-tooltip />
<el-table-column label="批次ID" align="center" prop="batchCode" width="120" />
<el-table-column label="仓库" align="center" prop="warehouseName" width="120" />
<el-table-column label="库位" align="center" prop="locationName" width="120" />
<el-table-column label="计划数量" align="center" prop="plannedQuantity" width="100" />
<el-table-column label="实际数量" align="center" prop="actualQuantity" width="100" />
<el-table-column label="单价" align="center" prop="unitPrice" width="100">
<template slot-scope="scope">
{{ formatAmount(scope.row.unitPrice) }}
</template>
</el-table-column>
<el-table-column label="金额" align="center" prop="totalAmount" width="120">
<template slot-scope="scope">
{{ formatAmount(scope.row.totalAmount || (scope.row.actualQuantity || 0) * (scope.row.unitPrice || 0)) }}
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="remark" min-width="150" show-overflow-tooltip />
</el-table>
<pagination
v-show="detailTotal > 0"
:total="detailTotal"
:page.sync="detailQueryParams.pageNum"
:limit.sync="detailQueryParams.pageSize"
@pagination="getDetailList"
style="margin-top: 15px;"
/>
<div slot="footer" class="dialog-footer">
<el-button @click="detailDialogVisible = false"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { listReturnOrderItems } from "@/api/inventory/return_order_items"
import RightToolbar from "@/components/RightToolbar"
import PageTitle from "@/components/PageTitle"
import OwnerSelector from "@/views/compononents/OwnerSelector.vue"
import WarehouseSelector from "@/views/compononents/WarehouseSelector.vue"
import LocationSelector from "@/views/compononents/LocationSelector.vue"
export default {
name: "ReturnOrderItems",
components: {
RightToolbar,
PageTitle,
OwnerSelector,
WarehouseSelector,
LocationSelector
},
data() {
return {
// 遮罩层
loading: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 退库物料表格数据
returnOrderItemList: [],
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
orderId: null,
startDate: null,
endDate: null,
ownerId: null,
materialId: null,
warehouseId: null,
warehousesCode: null,
locationId: null,
dateRange: null
},
// 货主选择相关
ownerSelectorVisible: false,
queryOwnerName: null,
// 仓库选择相关
warehouseSelectorVisible: false,
queryWarehouseName: null,
// 库位选择相关
locationSelectorVisible: false,
queryLocationName: null,
// 详情弹窗相关
detailDialogVisible: false,
detailLoading: false,
detailList: [],
detailTotal: 0,
currentDetailRow: null,
detailQueryParams: {
pageNum: 1,
pageSize: 10,
orderId: null,
materialId: null,
warehouseId: null,
warehousesCode: null,
locationId: null,
batchCode: null
},
// 详情弹窗中的仓库和库位选择
detailWarehouseSelectorVisible: false,
detailLocationSelectorVisible: false,
detailWarehouseName: null,
detailLocationName: null
}
},
created() {
this.getList()
},
methods: {
/** 查询退库物料列表 */
getList() {
this.loading = true
// 处理日期范围
const params = { ...this.queryParams }
if (params.dateRange && params.dateRange.length === 2) {
params.startDate = params.dateRange[0]
params.endDate = params.dateRange[1]
}
delete params.dateRange
listReturnOrderItems(params).then(response => {
this.returnOrderItemList = response.rows || []
this.total = response.total || 0
this.loading = false
}).catch(() => {
this.loading = false
})
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
/** 重置按钮操作 */
resetQuery() {
this.$refs.queryForm.resetFields()
this.queryOwnerName = null
this.queryWarehouseName = null
this.queryLocationName = null
this.queryParams.dateRange = null
this.queryParams.warehousesCode = null
this.handleQuery()
},
/** 打开货主选择器 */
openOwnerSelector() {
this.ownerSelectorVisible = true
},
/** 货主选择回调 */
handleOwnerSelected(owner) {
if (!owner) return
this.queryParams.ownerId = owner.ownerId || owner.ownerCode
this.queryOwnerName = owner.ownerName || owner.ownerCode
this.handleQuery()
},
/** 清空货主选择 */
clearQueryOwner() {
this.queryOwnerName = null
this.queryParams.ownerId = null
this.handleQuery()
},
/** 打开仓库选择器 */
openWarehouseSelector() {
this.warehouseSelectorVisible = true
},
/** 仓库选择回调 */
handleWarehouseSelected(warehouse) {
if (!warehouse) return
this.queryParams.warehouseId = warehouse.warehouseId || warehouse.id
this.queryParams.warehousesCode = warehouse.warehousesCode
this.queryWarehouseName = warehouse.warehousesName || warehouse.warehousesCode
// 仓库选择后,清空库位信息
this.queryLocationName = null
this.queryParams.locationId = null
this.handleQuery()
console.log('selected warehouse', warehouse);
},
/** 清空仓库选择 */
clearQueryWarehouse() {
this.queryWarehouseName = null
this.queryParams.warehouseId = null
this.queryParams.warehousesCode = null
// 清空仓库时,同时清空库位
this.queryLocationName = null
this.queryParams.locationId = null
this.handleQuery()
},
/** 打开库位选择器 */
openLocationSelector() {
if (!this.queryParams.warehouseId) {
this.$message.warning("请先选择仓库")
return
}
this.locationSelectorVisible = true
},
/** 库位选择回调 */
handleLocationSelected(location) {
if (!location) return
this.queryParams.locationId = location.locationId || location.id
this.queryLocationName = location.locationName || location.locationCode
this.handleQuery()
},
/** 清空库位选择 */
clearQueryLocation() {
this.queryLocationName = null
this.queryParams.locationId = null
this.handleQuery()
},
/** 格式化金额,保留2位小数 */
formatAmount(amount) {
if (amount === null || amount === undefined || isNaN(amount)) {
return '0.00'
}
return parseFloat(amount).toFixed(2)
},
/** 导出退库物料数据 */
handleExport() {
const params = { ...this.queryParams }
if (params.dateRange && params.dateRange.length === 2) {
params.startDate = params.dateRange[0]
params.endDate = params.dateRange[1]
}
delete params.dateRange
delete params.pageNum
delete params.pageSize
this.download('inventory/return_orders/export', {
...params
}, `退库单数据_${new Date().getTime()}.xlsx`)
},
/** 查看详情 */
handleViewDetail(row) {
this.currentDetailRow = row
this.detailDialogVisible = true
// 初始化详情查询参数
this.detailQueryParams = {
pageNum: 1,
pageSize: 10,
orderId: row.orderId,
materialId: row.materialId,
warehouseId: null,
warehousesCode: null,
locationId: null,
batchCode: null
}
this.detailWarehouseName = null
this.detailLocationName = null
this.detailList = []
this.detailTotal = 0
this.getDetailList()
},
/** 查询详情列表 */
getDetailList() {
this.detailLoading = true
const params = { ...this.detailQueryParams }
listReturnOrderItems(params).then(response => {
this.detailList = response.rows || []
this.detailTotal = response.total || 0
this.detailLoading = false
}).catch(() => {
this.detailLoading = false
})
},
/** 详情搜索按钮操作 */
handleDetailQuery() {
this.detailQueryParams.pageNum = 1
this.getDetailList()
},
/** 详情重置按钮操作 */
resetDetailQuery() {
this.detailQueryParams.warehouseId = null
this.detailQueryParams.warehousesCode = null
this.detailQueryParams.locationId = null
this.detailQueryParams.batchCode = null
this.detailWarehouseName = null
this.detailLocationName = null
this.detailQueryParams.pageNum = 1
this.getDetailList()
},
/** 打开详情弹窗中的仓库选择器 */
openDetailWarehouseSelector() {
this.detailWarehouseSelectorVisible = true
},
/** 详情弹窗中仓库选择回调 */
handleDetailWarehouseSelected(warehouse) {
if (!warehouse) return
this.detailQueryParams.warehouseId = warehouse.warehouseId || warehouse.id
this.detailQueryParams.warehousesCode = warehouse.warehousesCode
this.detailWarehouseName = warehouse.warehousesName || warehouse.warehousesCode
// 仓库选择后,清空库位信息
this.detailLocationName = null
this.detailQueryParams.locationId = null
},
/** 清空详情弹窗中的仓库选择 */
clearDetailWarehouse() {
this.detailWarehouseName = null
this.detailQueryParams.warehouseId = null
this.detailQueryParams.warehousesCode = null
// 清空仓库时,同时清空库位
this.detailLocationName = null
this.detailQueryParams.locationId = null
},
/** 打开详情弹窗中的库位选择器 */
openDetailLocationSelector() {
if (!this.detailQueryParams.warehousesCode) {
this.$message.warning("请先选择仓库")
return
}
this.detailLocationSelectorVisible = true
},
/** 详情弹窗中库位选择回调 */
handleDetailLocationSelected(location) {
if (!location) return
this.detailQueryParams.locationId = location.locationId || location.id
this.detailLocationName = location.locationName || location.locationCode
},
/** 清空详情弹窗中的库位选择 */
clearDetailLocation() {
this.detailLocationName = null
this.detailQueryParams.locationId = null
}
}
}
</script>
<style scoped>
/* 页面容器样式 */
.page-container {
padding: 16px;
background: #fff;
border-radius: 4px;
margin-bottom: 16px;
display: flex;
flex-direction: column;
height: calc(100vh - 140px);
min-height: 600px;
}
/* 表格容器样式 */
.table-container {
flex: 1;
min-height: 0;
margin-top: 16px;
display: flex;
flex-direction: column;
overflow-x: auto;
}
.mb8 {
margin-bottom: 8px;
}
</style>
......@@ -23,5 +23,16 @@
<artifactId>ruoyi-common</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.3.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.32</version>
</dependency>
</dependencies>
</project>
\ No newline at end of file
......@@ -14,11 +14,13 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.alibaba.excel.EasyExcel;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.inventory.domain.Inventory;
import com.ruoyi.inventory.domain.vo.InventorySummaryVO;
import com.ruoyi.inventory.service.IInventoryService;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.core.page.TableDataInfo;
......@@ -48,6 +50,28 @@ public class InventoryController extends BaseController
return getDataTable(list);
}
/**
* 查询库存列表(按物料汇总统计)
*/
@PreAuthorize("@ss.hasPermi('inventory:inventory:list')")
@GetMapping("/listCount")
public TableDataInfo listCount(Inventory inventory)
{
startPage();
List<InventorySummaryVO> list = inventoryService.selectInventorySummaryList(inventory);
return getDataTable(list);
}
/**
* 查询库存明细列表(根据物料标识及检索条件)
*/
@PreAuthorize("@ss.hasPermi('inventory:inventory:list')")
@GetMapping("/detailList")
public TableDataInfo detailList(Inventory inventory)
{
List<Inventory> list = inventoryService.selectInventoryDetailList(inventory);
return getDataTable(list);
}
/**
* 查询库存列表
*/
@PreAuthorize("@ss.hasPermi('inventory:inventory:list')")
......@@ -70,6 +94,27 @@ public class InventoryController extends BaseController
ExcelUtil<Inventory> util = new ExcelUtil<Inventory>(Inventory.class);
util.exportExcel(response, list, "库存数据");
}
/**
* 导出库存列表(使用EasyExcel)
*/
@PreAuthorize("@ss.hasPermi('inventory:inventory:export')")
@Log(title = "库存", businessType = BusinessType.EXPORT)
@PostMapping("/exportExcel")
public void exportExcel(HttpServletResponse response, Inventory inventory)
{
try {
List<InventorySummaryVO> list = inventoryService.selectInventorySummaryList(inventory);
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
String fileName = "库存明细数据_" + System.currentTimeMillis() + ".xlsx";
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + java.net.URLEncoder.encode(fileName, "UTF-8"));
EasyExcel.write(response.getOutputStream(), InventorySummaryVO.class)
.sheet("库存明细")
.doWrite(list);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取库存详细信息
......
......@@ -38,6 +38,9 @@ public class Inventory extends BaseEntity
/** 批次ID 检索条件 */
@Excel(name = "仓库ID ")
private String warehousesId;
/** 仓库编码 检索条件 */
private String warehousesCode;
/** 库位ID 检索条件 */
@Excel(name = "库位ID 检索条件")
private String locationId;
......@@ -106,6 +109,9 @@ public class Inventory extends BaseEntity
@Excel(name = "排序号")
private String updateUserCode;
/** 预警类型 */
private String alertType;
public String getWarehousesId() {
return warehousesId;
}
......@@ -324,6 +330,26 @@ public class Inventory extends BaseEntity
return updateUserCode;
}
public void setWarehousesCode(String warehousesCode)
{
this.warehousesCode = warehousesCode;
}
public String getWarehousesCode()
{
return warehousesCode;
}
public void setAlertType(String alertType)
{
this.alertType = alertType;
}
public String getAlertType()
{
return alertType;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
......
......@@ -80,4 +80,20 @@ public interface InventoryMapper
* @version 1.0
*/
List<StocktakeItems> selectstocktakeItemsList();
/**
* 按物料汇总统计库存
*
* @param inventory 库存查询条件
* @return 库存汇总统计集合
*/
public List<com.ruoyi.inventory.domain.vo.InventorySummaryVO> selectInventorySummaryList(Inventory inventory);
/**
* 查询库存明细列表(根据物料标识及检索条件)
*
* @param inventory 库存查询条件
* @return 库存明细集合
*/
public List<Inventory> selectInventoryDetailList(Inventory inventory);
}
......@@ -90,5 +90,19 @@ public interface IInventoryService
*/
public List<StocktakeItems> selectstocktakeItemsList();
/**
* 按物料汇总统计库存
*
* @param inventory 库存查询条件
* @return 库存汇总统计集合
*/
public List<com.ruoyi.inventory.domain.vo.InventorySummaryVO> selectInventorySummaryList(Inventory inventory);
/**
* 查询库存明细列表(根据物料标识及检索条件)
*
* @param inventory 库存查询条件
* @return 库存明细集合
*/
public List<Inventory> selectInventoryDetailList(Inventory inventory);
}
......@@ -205,4 +205,28 @@ public class InventoryServiceImpl implements IInventoryService
public List<StocktakeItems> selectstocktakeItemsList(){
return inventoryMapper.selectstocktakeItemsList();
}
/**
* 按物料汇总统计库存
*
* @param inventory 库存查询条件
* @return 库存汇总统计集合
*/
@Override
public List<com.ruoyi.inventory.domain.vo.InventorySummaryVO> selectInventorySummaryList(Inventory inventory)
{
return inventoryMapper.selectInventorySummaryList(inventory);
}
/**
* 查询库存明细列表(根据物料标识及检索条件)
*
* @param inventory 库存查询条件
* @return 库存明细集合
*/
@Override
public List<Inventory> selectInventoryDetailList(Inventory inventory)
{
return inventoryMapper.selectInventoryDetailList(inventory);
}
}
......@@ -10,6 +10,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<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" />
......@@ -54,6 +55,24 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</resultMap>
<resultMap type="com.ruoyi.inventory.domain.vo.InventorySummaryVO" id="InventorySummaryResult">
<result property="materialId" column="material_id" />
<result property="materialName" column="material_name" />
<result property="inventoryTypeName" column="inventory_type_name" />
<result property="warehousesCode" column="warehouses_code" />
<result property="warehouseName" column="warehouse_name" />
<result property="locationId" column="location_id" />
<result property="locationName" column="location_name" />
<result property="ownerId" column="owner_id" />
<result property="ownerName" column="owner_name" />
<result property="totalQuantity" column="total_quantity" />
<result property="totalLockedQuantity" column="total_locked_quantity" />
<result property="totalAvailableQuantity" column="total_available_quantity" />
<result property="totalWeight" column="total_weight" />
<result property="totalVolume" column="total_volume" />
<result property="inventoryStatusName" column="inventory_status_name" />
</resultMap>
<sql id="selectInventoryVo">
select id, inventory_type,warehouses_id, order_id, material_id, batch_id, location_id, owner_id, quantity, locked_quantity, unit_weight, total_weight, total_volume, production_date, expiration_date, inventory_status, last_inbound_time, last_outbound_time, is_used, sort_no, create_time, create_user_code, update_time, update_user_code from inventory
</sql>
......@@ -85,6 +104,62 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</where>
</select>
<!-- 按物料汇总统计库存 -->
<select id="selectInventorySummaryList" parameterType="Inventory" resultMap="InventorySummaryResult">
select
i.material_id,
m.material_name,
case i.inventory_type when 1 then '普通' when 2 then '退库' else '未知' end as inventory_type_name,
i.warehouses_code,
w.warehouses_name as warehouse_name,
i.location_id,
sl.location_name,
i.owner_id,
o.owner_name,
sum(i.quantity) as total_quantity,
sum(i.locked_quantity) as total_locked_quantity,
sum(i.quantity - ifnull(i.locked_quantity, 0)) as total_available_quantity,
sum(ifnull(i.total_weight, 0)) as total_weight,
sum(ifnull(i.total_volume, 0)) as total_volume,
case max(i.inventory_status) when 0 then '已出库' when 1 then '正常' else '未知' end as inventory_status_name
from inventory i
left join materials m on i.material_id = m.material_code
left join warehouses w on i.warehouses_code = w.warehouses_code
left join storage_locations sl on i.location_id = sl.location_code
left join owners o on i.owner_id = o.owner_code
<where>
<if test="inventoryType != null "> and i.inventory_type = #{inventoryType}</if>
<if test="materialId != null and materialId != ''"> and (i.material_id like concat('%', #{materialId}, '%') or m.material_code like concat('%', #{materialId}, '%'))</if>
<if test="batchId != null and batchId != ''"> and i.batch_id = #{batchId}</if>
<if test="warehousesCode != null and warehousesCode != ''"> and i.warehouses_code = #{warehousesCode}</if>
<if test="locationId != null and locationId != ''"> and i.location_id = #{locationId}</if>
<if test="ownerId != null and ownerId != ''"> and i.owner_id = #{ownerId}</if>
<if test="inventoryStatus != null "> and i.inventory_status = #{inventoryStatus}</if>
<if test="isUsed != null "> and i.is_used = #{isUsed}</if>
<!-- 预警类型过滤(需要根据业务逻辑判断,这里暂时不实现) -->
</where>
group by i.material_id, i.warehouses_code, i.location_id, i.owner_id, i.inventory_type, i.inventory_status
order by i.material_id, i.warehouses_code, i.location_id
</select>
<!-- 查询库存明细列表(根据物料标识及检索条件) -->
<select id="selectInventoryDetailList" parameterType="Inventory" resultMap="InventoryResult">
<include refid="selectInventoryVo"/>
<where>
<if test="inventoryType != null "> and inventory_type = #{inventoryType}</if>
<if test="orderId != null and orderId != ''"> and order_id = #{orderId}</if>
<if test="materialId != null and materialId != ''"> and material_id = #{materialId}</if>
<if test="batchId != null and batchId != ''"> and batch_id = #{batchId}</if>
<if test="warehousesCode != null and warehousesCode != ''"> and warehouses_code = #{warehousesCode}</if>
<if test="locationId != null and locationId != ''"> and location_id = #{locationId}</if>
<if test="ownerId != null and ownerId != ''"> and owner_id = #{ownerId}</if>
<if test="inventoryStatus != null "> and inventory_status = #{inventoryStatus}</if>
<if test="isUsed != null "> and is_used = #{isUsed}</if>
<if test="warehousesId != null "> and warehouses_id = #{warehousesId}</if>
</where>
order by material_id, warehouses_code, location_id, create_time desc
</select>
<select id="selectInventory" parameterType="Inventory" resultMap="InventoryResult">
<include refid="selectInventoryVo"/>
<where>
......@@ -92,6 +167,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="orderId != null and orderId != ''"> and order_id = #{orderId}</if>
<if test="materialId != null and materialId != ''"> and material_id = #{materialId}</if>
<if test="batchId != null and batchId != ''"> and batch_id = #{batchId}</if>
<if test="warehousesCode != null and warehousesCode != ''"> and warehouses_code = #{warehousesCode}</if>
<if test="locationId != null and locationId != ''"> and location_id = #{locationId}</if>
<if test="ownerId != null and ownerId != ''"> and owner_id = #{ownerId}</if>
<if test="quantity != null "> and quantity = #{quantity}</if>
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论