Browse Source

feat(qc): 新增呆滞品临期品库存管理功能

- 新增呆滞品/临期品库存查询接口和API
- 在质检结果录入页面增加批次明细查看功能
- 扩展质检数据模型增加批次和库位信息字段
- 实现批次明细查询参数传递和数据显示
- 新建呆滞品临期品库存管理页面组件
- 添加袋装明细查看和批量送检功能
- 实现库存按类型筛选和分页显示
- 集成库存管理和质检流程的关联功能
master
qiankanghui 1 week ago
parent
commit
53c29c8844
  1. 10
      src/api/pms/inventoryStock.js
  2. 45
      src/views/modules/qc/SQCResultEntry.vue
  3. 333
      src/views/modules/qc/stagnationExpiredStock.vue

10
src/api/pms/inventoryStock.js

@ -0,0 +1,10 @@
import { createAPI } from "@/utils/httpRequest.js";
// 查询呆滞品/临期品库存列表
export const queryStagnationExpiredStock = data => createAPI(`/pms/inventorystock/queryStagnationExpiredStock`, 'post', data);
// 查询袋装明细列表
export const queryPrintLabelBagList = data => createAPI(`/pms/inventorystock/queryPrintLabelBagList`, 'post', data);
// 批量送检临期品
export const batchSendExpiredForInspection = data => createAPI(`/pms/inventorystock/batchSendExpiredForInspection`, 'post', data);

45
src/views/modules/qc/SQCResultEntry.vue

@ -161,12 +161,13 @@
fixed="right"
header-align="center"
align="center"
width="130"
width="200"
label="操作">
<template slot-scope="scope">
<el-link style="cursor: pointer" v-if="!authDetail && scope.row.state === '未开始'" @click="actionModal(scope.row)">开始检验</el-link>
<el-link style="cursor: pointer" v-if="!authDetail && scope.row.state !== '未开始'" @click="detailModal(scope.row)">检验单</el-link>
<el-link style="cursor: pointer" v-if="!authFile" @click="getFileContentData(scope.row)">工作文件</el-link>
<el-link style="cursor: pointer" @click="showBatchDetails(scope.row)">批次明细</el-link>
</template>
</el-table-column>
</el-table>
@ -288,9 +289,6 @@
<el-input v-else v-model="detailData.disposalRemark" style="width: 293px"></el-input>
</el-form-item>
</el-form-item>
<el-form-item label=" " >
<el-button type="primary" @click="showBatchDetails">批次明细</el-button>
</el-form-item>
</el-form>
<div class="rq">
<el-table
@ -829,7 +827,11 @@
notPassQty: 0,
batchQualifiedQty: 0,
notbatchQualifiedQty: 0,
refInfo1: ''
refInfo1: '',
refInfo2: '',
batchNo: '',
warehouseId: '',
locationId: ''
},
saveInformationData: {
site: '',
@ -853,7 +855,9 @@
type: '',
passQty: '',
notPassQty: '',
batchQualifiedQty: ''
batchQualifiedQty: '',
refInfo1: '',
refInfo2: ''
},
fileFlag: false,
fileColumnList: [
@ -1865,6 +1869,9 @@
this.detailData.responsiblePersonName = row.responsiblePersonName
this.detailData.state = row.state
this.detailData.refInfo1 = row.refInfo1
this.detailData.batchNo = row.batchNo
this.detailData.warehouseId = row.warehouseId
this.detailData.locationId = row.locationId
if (row.state === '待检验') {
this.detailData.passQty = row.samplingQty
} else {
@ -1982,7 +1989,16 @@
this.saveInformationData.partDesc = this.detailData.partDesc
this.saveInformationData.operator = this.detailData.operator
this.saveInformationData.responsiblePerson = this.detailData.responsiblePerson
this.saveInformationData.refInfo1 = this.detailData.refInfo1
this.saveInformationData.refInfo2 = this.detailData.refInfo2
this.saveInformationData.itemList = JSON.parse(JSON.stringify(this.detailList))
// refInfo1refInfo2
if (this.saveInformationData.itemList && this.saveInformationData.itemList.length > 0) {
this.saveInformationData.itemList.forEach(item => {
item.refInfo1 = this.detailData.refInfo1
item.refInfo2 = this.detailData.refInfo2
})
}
this.saveInformationData.passQty = this.detailData.passQty == null ? 0 : this.detailData.passQty
this.saveInformationData.notPassQty = this.detailData.notPassQty == null ? 0 : this.detailData.notPassQty
this.saveInformationData.batchQualifiedQty = this.detailData.batchQualifiedQty == null ? 0 : this.detailData.batchQualifiedQty
@ -2672,14 +2688,23 @@
getBaseData () {},
//
showBatchDetails () {
if (!this.detailData.refInfo1) {
showBatchDetails (row) {
const rowData = row || this.detailData
if (!rowData.refInfo1) {
this.$message.warning('实验室单号(refInfo1)不能为空!')
return
}
this.batchDetailLoading = true
this.batchDetailFlag = true
getBatchDetailsByLaboratoryOrder({ laboratoryOrderNo: this.detailData.refInfo1 }).then(({data}) => {
const params = {
laboratoryOrderNo: rowData.refInfo1,
site: rowData.site,
partNo: rowData.partNo,
batchNo: rowData.batchNo,
warehouseId: rowData.warehouseId,
locationId: rowData.locationId
}
getBatchDetailsByLaboratoryOrder(params).then(({data}) => {
this.batchDetailLoading = false
if (data && data.code === 0) {
this.batchDetailList = data.rows || []
@ -2700,6 +2725,8 @@
created () {
this.getButtonAuthData()
this.disposalMeasuresSearch()
//
this.searchData.states = ['未开始', '待检验']
if (!this.authSearch) {
this.getDataList()
}

333
src/views/modules/qc/stagnationExpiredStock.vue

@ -0,0 +1,333 @@
<template>
<div>
<el-form :model="searchData" label-position="top" style="max-width: 1400px">
<el-row :gutter="5">
<el-col :span="2">
<el-form-item label="工厂编码">
<el-input v-model="searchData.site" clearable></el-input>
</el-form-item>
</el-col>
<el-col :span="3">
<el-form-item label="物料编码">
<el-input v-model="searchData.partNo" clearable></el-input>
</el-form-item>
</el-col>
<el-col :span="3">
<el-form-item label="物料名称">
<el-input v-model="searchData.partDescription" clearable></el-input>
</el-form-item>
</el-col>
<el-col :span="2">
<el-form-item label="批次号">
<el-input v-model="searchData.batchNo" clearable></el-input>
</el-form-item>
</el-col>
<el-col :span="2">
<el-form-item label="仓库">
<el-input v-model="searchData.warehouseName" clearable></el-input>
</el-form-item>
</el-col>
<el-col :span="2">
<el-form-item label="库位">
<el-input v-model="searchData.locationId" clearable></el-input>
</el-form-item>
</el-col>
<el-col :span="2">
<el-form-item label="临期天数">
<el-input
v-model="searchData.daysToExpire"
oninput="value=value.replace(/[^\d]/g,'')"
clearable>
</el-input>
</el-form-item>
</el-col>
<el-col :span="2">
<el-form-item label="呆滞天数">
<el-input
v-model="searchData.stagnationDays"
oninput="value=value.replace(/[^\d]/g,'')"
clearable>
</el-input>
</el-form-item>
</el-col>
<el-col :span="2">
<el-form-item label="类型">
<el-select v-model="searchData.type" clearable>
<el-option value="" label="全部"></el-option>
<el-option value="STAGNATION" label="呆滞品"></el-option>
<el-option value="EXPIRED" label="临期品"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item label=" ">
<el-button type="primary" @click="search">查询</el-button>
<el-button @click="reset">重置</el-button>
<el-button type="success" @click="batchSendInspection">批量送检</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
<el-table :data="tableData" border v-loading="loading" height="calc(100vh - 190px)" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center"/>
<el-table-column label="操作" width="100" align="center">
<template slot-scope="scope">
<a @click="showBagDetail(scope.row)">袋装明细</a>
</template>
</el-table-column>
<el-table-column prop="site" label="工厂编码" align="left" header-align="center" min-width="80"/>
<el-table-column prop="warehouseId" label="仓库ID" align="left" header-align="center" min-width="100"/>
<el-table-column prop="warehouseName" label="仓库名称" align="left" header-align="center" min-width="120"/>
<el-table-column prop="partNo" label="物料编码" align="left" header-align="center" min-width="120"/>
<el-table-column prop="partDescription" label="物料名称" align="left" header-align="center" min-width="200"/>
<el-table-column prop="batchNo" label="批次号" align="left" header-align="center" min-width="120"/>
<el-table-column prop="type" label="类型" align="center" header-align="center" min-width="100">
<template slot-scope="scope">
<el-tag
:type="scope.row.type === 'STAGNATION' ? 'warning' : 'danger'"
size="small">
{{ scope.row.type === 'STAGNATION' ? '呆滞品' : '临期品' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="locationId" label="库位" align="left" header-align="center" min-width="100"/>
<el-table-column prop="qtyOnHand" label="库存数量" align="right" header-align="center" min-width="100"/>
<el-table-column prop="firstInDate" label="入库日期" align="left" header-align="center" min-width="110"/>
<el-table-column prop="latestOutDate" label="最后出库日期" align="left" header-align="center" min-width="110"/>
<el-table-column prop="latestInDate" label="最后入库日期" align="left" header-align="center" min-width="110"/>
<el-table-column prop="expiredDate" label="失效日期" align="left" header-align="center" min-width="110"/>
<el-table-column prop="stagnationStartDate" label="呆滞起算日" align="left" header-align="center" min-width="120"/>
<el-table-column prop="stagnationDays" label="呆滞天数" align="right" header-align="center" min-width="100"/>
<el-table-column prop="daysToExpire" label="距过期天数" align="right" header-align="center" min-width="100"/>
<el-table-column prop="expireDateControlFlag" label="有效期管理" align="center" header-align="center" min-width="120">
<template slot-scope="scope">
{{ scope.row.expireDateControlFlag === 'Y' ? '是' : '否' }}
</template>
</el-table-column>
<el-table-column prop="toExpireDays" label="有效期天数" align="right" header-align="center" min-width="100"/>
</el-table>
<el-pagination
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
:current-page.sync="pageIndex"
:page-sizes="[10, 20, 50, 100, 200]"
:page-size="pageSize"
layout="total,sizes, prev, pager, next"
:total="totalPage">
</el-pagination>
<el-dialog :visible.sync="bagDetailVisible" v-drag top="5vh" title="袋装明细" width="80%" :close-on-click-modal="false">
<el-table :data="bagDetailData" border v-loading="bagDetailLoading" height="400px">
<el-table-column prop="site" label="工厂编码" align="left" header-align="center" min-width="80"/>
<el-table-column prop="partNo" label="物料编码" align="left" header-align="center" min-width="120"/>
<el-table-column prop="partDescription" label="物料名称" align="left" header-align="center" min-width="200"/>
<el-table-column prop="seqNo" label="Seq No" align="left" header-align="center" min-width="100"/>
<el-table-column prop="itemNo" label="序号" align="left" header-align="center" min-width="60"/>
<el-table-column prop="printQty" label="数量" align="right" header-align="center" min-width="80"/>
<el-table-column prop="batchNo" label="批次号" align="left" header-align="center" min-width="120"/>
<el-table-column prop="warehouseName" label="仓库" align="left" header-align="center" min-width="120"/>
<el-table-column prop="locationId" label="库位" align="left" header-align="center" min-width="100"/>
<el-table-column prop="status" label="状态" align="center" header-align="center" min-width="80"/>
<el-table-column prop="weight" label="重量" align="right" header-align="center" min-width="80"/>
<el-table-column prop="createTime" label="创建时间" align="center" header-align="center" min-width="120"/>
<el-table-column prop="updateTime" label="入库时间" align="center" header-align="center" min-width="120"/>
</el-table>
<span slot="footer" class="dialog-footer" style="text-align: center">
<el-button type="primary" @click="bagDetailVisible = false">关闭</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { queryStagnationExpiredStock, queryPrintLabelBagList, batchSendExpiredForInspection } from '@/api/pms/inventoryStock'
export default {
data() {
return {
searchData: {
site: '',
partNo: '',
partDescription: '',
batchNo: '',
warehouseName: '',
locationId: '',
daysToExpire: '',
stagnationDays: '',
type: ''
},
tableData: [],
pageIndex: 1,
pageSize: 50,
totalPage: 0,
loading: false,
//
bagDetailVisible: false,
bagDetailLoading: false,
bagDetailData: [],
currentRow: {},
//
selectedRows: []
}
},
mounted() {
//
},
methods: {
search() {
//
if (!this.searchData.site || this.searchData.site.trim() === '') {
this.$message.warning('工厂编码为必填项')
return
}
this.pageIndex = 1
this.getDataList()
},
reset() {
this.searchData = {
site: '',
partNo: '',
partDescription: '',
batchNo: '',
warehouseName: '',
locationId: '',
daysToExpire: '',
stagnationDays: '',
type: ''
}
this.pageIndex = 1
this.getDataList()
},
getDataList() {
this.loading = true
const params = {
...this.searchData,
page: this.pageIndex,
limit: this.pageSize
}
queryStagnationExpiredStock(params).then(({ data: res }) => {
this.loading = false
if (res.code !== 0) {
return this.$message.error(res.msg)
}
this.tableData = res.page.list || []
this.totalPage = res.page.totalCount || 0
}).catch(() => {
this.loading = false
})
},
sizeChangeHandle(val) {
this.pageSize = val
this.pageIndex = 1
this.getDataList()
},
currentChangeHandle(val) {
this.pageIndex = val
this.getDataList()
},
//
showBagDetail(row) {
this.currentRow = row
this.bagDetailVisible = true
this.getBagDetailList()
},
//
getBagDetailList() {
this.bagDetailLoading = true
const params = {
site: this.currentRow.site,
partNo: this.currentRow.partNo,
batchNo: this.currentRow.batchNo,
warehouseId: this.currentRow.warehouseId,
locationId: this.currentRow.locationId,
page: 1,
limit: 100
}
queryPrintLabelBagList(params).then(({ data: res }) => {
this.bagDetailLoading = false
if (res.code !== 0) {
return this.$message.error(res.msg)
}
this.bagDetailData = res.page.list || []
}).catch(() => {
this.bagDetailLoading = false
})
},
//
handleSelectionChange(selection) {
this.selectedRows = selection
},
//
batchSendInspection() {
//
if (!this.selectedRows || this.selectedRows.length === 0) {
this.$message.warning('请至少选择一条数据进行送检')
return
}
//
const expiredRows = this.selectedRows.filter(row => row.type === 'EXPIRED')
if (expiredRows.length === 0) {
this.$message.warning('只能选择临期品进行送检')
return
}
//
if (expiredRows.length < this.selectedRows.length) {
this.$message.warning(`已自动过滤 ${this.selectedRows.length - expiredRows.length} 条非临期品数据,仅送检临期品`)
}
//
this.$confirm(`确认将选中的 ${expiredRows.length} 条临期品数据进行送检?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
const sendParams = expiredRows.map(row => ({
site: row.site,
partNo: row.partNo,
batchNo: row.batchNo,
warehouseId: row.warehouseId,
locationId: row.locationId
}))
if (sendParams.length === 0) {
this.$message.error('选中的数据缺少有效信息')
return
}
this.loading = true
batchSendExpiredForInspection(sendParams).then(({ data: res }) => {
this.loading = false
if (res.code === 0) {
this.$message.success(res.msg || '批量送检成功')
//
this.selectedRows = []
//
this.getDataList()
} else {
this.$message.error(res.msg || '批量送检失败')
}
}).catch((error) => {
this.loading = false
this.$message.error('批量送检异常: ' + (error.message || '未知错误'))
})
}).catch(() => {
//
})
}
}
}
</script>
<style scoped>
</style>
Loading…
Cancel
Save