feat: 打通出库作业链路

This commit is contained in:
zgw
2026-06-24 09:13:18 +08:00
parent a3c023a8d3
commit 1af5b3cbd3
141 changed files with 17337 additions and 1065 deletions

View File

@@ -38,6 +38,7 @@
"jsbarcode": "^3.12.3",
"jsencrypt": "3.0.0-rc.1",
"nprogress": "0.2.0",
"qrcode": "^1.5.4",
"quill": "2.0.2",
"screenfull": "5.0.2",
"sortablejs": "1.10.2",

View File

@@ -35,6 +35,15 @@ export function addBarcodeDataBatch(dataList) {
})
}
// 按条码样式和数量生成连续条码
export function generateBarcodeData(data) {
return request({
url: '/bas/barcodeData/generate',
method: 'post',
data: data
})
}
// 修改基础信息-条码数据
export function updateBarcodeData(data) {
return request({

View File

@@ -0,0 +1,44 @@
import request from '@/utils/request'
// 查询入口/出口点位配置列表
export function listStationPoint(query) {
return request({
url: '/bas/stationpoint/list',
method: 'get',
params: query
})
}
// 查询入口/出口点位配置详细
export function getStationPoint(id) {
return request({
url: '/bas/stationpoint/' + id,
method: 'get'
})
}
// 新增入口/出口点位配置
export function addStationPoint(data) {
return request({
url: '/bas/stationpoint',
method: 'post',
data: data
})
}
// 修改入口/出口点位配置
export function updateStationPoint(data) {
return request({
url: '/bas/stationpoint',
method: 'put',
data: data
})
}
// 删除入口/出口点位配置
export function delStationPoint(id) {
return request({
url: '/bas/stationpoint/' + id,
method: 'delete'
})
}

View File

@@ -42,3 +42,46 @@ export function delStock(stockId) {
method: 'delete'
})
}
// 查询库存物料明细列表
export function listStockDetail(query) {
return request({
url: '/bas/stock/detail/list',
method: 'get',
params: query
})
}
// 查询库存物料明细详细
export function getStockDetail(detailId) {
return request({
url: '/bas/stock/detail/' + detailId,
method: 'get'
})
}
// 新增库存物料明细
export function addStockDetail(data) {
return request({
url: '/bas/stock/detail',
method: 'post',
data: data
})
}
// 修改库存物料明细
export function updateStockDetail(data) {
return request({
url: '/bas/stock/detail',
method: 'put',
data: data
})
}
// 删除库存物料明细
export function delStockDetail(detailId) {
return request({
url: '/bas/stock/detail/' + detailId,
method: 'delete'
})
}

View File

@@ -0,0 +1,44 @@
import request from '@/utils/request'
// 查询托盘入库规则列表
export function listTrayRule(query) {
return request({
url: '/bas/trayRule/list',
method: 'get',
params: query
})
}
// 查询托盘入库规则详细
export function getTrayRule(id) {
return request({
url: '/bas/trayRule/' + id,
method: 'get'
})
}
// 新增托盘入库规则
export function addTrayRule(data) {
return request({
url: '/bas/trayRule',
method: 'post',
data: data
})
}
// 修改托盘入库规则
export function updateTrayRule(data) {
return request({
url: '/bas/trayRule',
method: 'put',
data: data
})
}
// 删除托盘入库规则
export function delTrayRule(id) {
return request({
url: '/bas/trayRule/' + id,
method: 'delete'
})
}

View File

@@ -58,3 +58,11 @@ export function completeJob(jobId) {
method: 'put'
})
}
// 推送出库作业到 WCS
export function pushJobToWcs(jobId) {
return request({
url: '/out/job/pushWcs/' + jobId,
method: 'put'
})
}

View File

@@ -42,6 +42,16 @@
v-hasPermi="['bas:barcodeData:add']"
>新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-printer"
size="mini"
:disabled="multiple"
@click="handleBatchPrint"
>批量打印</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
@@ -146,19 +156,12 @@
</el-form-item>
</el-col>
<!-- 单条录入时显示打印值 -->
<el-col :span="24" v-if="!addForm.useRange">
<el-form-item label="打印值" prop="barcodeNo">
<el-input v-model="addForm.barcodeNo" placeholder="请输入打印值" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="打印数量" prop="printNum">
<el-form-item label="生成数量" prop="printNum">
<el-input-number
v-model="addForm.printNum"
:min="1"
:max="9999"
:max="10000"
controls-position="right"
style="width:100%"
/>
@@ -171,55 +174,6 @@
</el-form-item>
</el-col>
<!-- 分割线:生成范围 -->
<el-col :span="24">
<el-divider content-position="left">
<el-checkbox v-model="addForm.useRange" @change="onUseRangeChange">按范围批量生成</el-checkbox>
</el-divider>
</el-col>
<el-col :span="24" v-if="addForm.useRange">
<el-form-item label="生成范围">
<el-input-number
v-model="addForm.rangeStart"
:min="0"
:max="999999"
controls-position="right"
placeholder="起始号"
style="width:45%"
/>
<span style="margin:0 8px;color:#606266">~</span>
<el-input-number
v-model="addForm.rangeEnd"
:min="0"
:max="999999"
controls-position="right"
placeholder="结束号"
style="width:45%"
/>
</el-form-item>
<el-form-item label="号码前缀">
<el-input v-model="addForm.rangePrefix" placeholder="可选,如:WH-" style="width:100%" />
</el-form-item>
<el-form-item label="补零位数">
<el-input-number
v-model="addForm.rangeDigits"
:min="0"
:max="10"
controls-position="right"
placeholder="0 表示不补零"
style="width:100%"
/>
</el-form-item>
<el-form-item label="" label-width="100px">
<el-tag type="info" size="small" v-if="rangeCount > 0">
将生成 {{ rangeCount }} 条记录
</el-tag>
<el-tag type="warning" size="small" v-if="rangeCount > 500">
数量较多,请谨慎操作
</el-tag>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
@@ -290,8 +244,8 @@
<script>
import JsBarcode from 'jsbarcode'
import { listBarcodeData, getBarcodeData, delBarcodeData, addBarcodeData, addBarcodeDataBatch, updateBarcodeData } from "@/api/bas/barcodeData"
import { listBarcodeFormat, getBarcodeFormat } from "@/api/bas/barcodeFormat"
import { listBarcodeData, getBarcodeData, delBarcodeData, generateBarcodeData, updateBarcodeData } from "@/api/bas/barcodeData"
import { listBarcodeFormat } from "@/api/bas/barcodeFormat"
export default {
name: "BarcodeData",
@@ -299,6 +253,7 @@ export default {
return {
loading: true,
ids: [],
selectedRows: [],
single: true,
multiple: true,
showSearch: true,
@@ -310,7 +265,7 @@ export default {
addForm: {},
addRules: {
formatId: [{ required: true, message: '请选择条码样式', trigger: 'change' }],
barcodeNo: [{ required: true, message: '请输入打印值', trigger: 'blur' }]
printNum: [{ required: true, message: '请输入生成数量', trigger: 'blur' }]
},
// 修改弹窗
editOpen: false,
@@ -332,15 +287,6 @@ export default {
}
}
},
computed: {
rangeCount() {
if (!this.addForm.useRange) return 0
const s = Number(this.addForm.rangeStart)
const e = Number(this.addForm.rangeEnd)
if (isNaN(s) || isNaN(e) || e < s) return 0
return e - s + 1
}
},
created() {
this.getList()
this.loadFormatOptions()
@@ -368,14 +314,8 @@ export default {
resetAddForm() {
this.addForm = {
formatId: null,
barcodeNo: null,
printNum: 1,
remark: null,
useRange: false,
rangeStart: null,
rangeEnd: null,
rangePrefix: '',
rangeDigits: 0
remark: null
}
if (this.$refs.addFormRef) {
this.$refs.addFormRef.resetFields()
@@ -387,74 +327,25 @@ export default {
this.addOpen = true
},
onUseRangeChange(val) {
if (val) {
this.addForm.barcodeNo = null
} else {
this.addForm.rangeStart = null
this.addForm.rangeEnd = null
this.addForm.rangePrefix = ''
}
},
submitAddForm() {
// 范围模式下不校验 barcodeNo
if (this.addForm.useRange) {
this.$delete(this.addRules, 'barcodeNo')
} else {
this.$set(this.addRules, 'barcodeNo', [{ required: true, message: '请输入打印值', trigger: 'blur' }])
}
this.$nextTick(() => {
this.$refs.addFormRef.validate(valid => {
if (!valid) return
// 范围批量生成
if (this.addForm.useRange) {
const s = Number(this.addForm.rangeStart)
const e = Number(this.addForm.rangeEnd)
if (isNaN(s) || isNaN(e) || e < s) {
this.$modal.msgWarning('请正确填写生成范围(起始号 ≤ 结束号)')
return
}
if (this.rangeCount > 1000) {
this.$modal.msgWarning('单次生成不得超过 1000 条,请缩小范围')
return
}
const prefix = this.addForm.rangePrefix || ''
const digits = Number(this.addForm.rangeDigits) || 0
const batchList = []
for (let i = s; i <= e; i++) {
const seq = digits > 0 ? String(i).padStart(digits, '0') : String(i)
batchList.push({
formatId: this.addForm.formatId,
barcodeNo: prefix + seq,
printNum: this.addForm.printNum || 1,
remark: this.addForm.remark
})
}
addBarcodeDataBatch(batchList).then(() => {
this.$modal.msgSuccess(`批量生成 ${batchList.length} 条成功`)
this.addOpen = false
this.getList()
}).catch(() => {
this.$modal.msgError('批量生成失败,请重试')
})
return
}
// 单条新增
const submitData = {
this.$refs.addFormRef.validate(valid => {
if (!valid) return
const doGenerate = () => {
generateBarcodeData({
formatId: this.addForm.formatId,
barcodeNo: this.addForm.barcodeNo,
printNum: this.addForm.printNum,
remark: this.addForm.remark
}
addBarcodeData(submitData).then(() => {
this.$modal.msgSuccess("新增成功")
}).then(response => {
this.$modal.msgSuccess(response.msg || '生成成功')
this.addOpen = false
this.getList()
})
})
}
if (Number(this.addForm.printNum) >= 1000) {
this.$modal.confirm('本次将生成' + this.addForm.printNum + '条条码,生成后只刷新分页列表,不会一次性渲染全部条码,是否继续?').then(doGenerate).catch(() => {})
} else {
doGenerate()
}
})
},
@@ -514,6 +405,7 @@ export default {
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id)
this.selectedRows = selection
this.single = selection.length !== 1
this.multiple = !selection.length
},
@@ -534,29 +426,48 @@ export default {
}, `barcodeData_${new Date().getTime()}.xlsx`)
},
handleBatchPrint() {
this.printRows(this.selectedRows)
},
/** 打印按钮操作 */
handlePrint(row) {
const barcodeValue = row.barcodeNo || ''
if (!barcodeValue) {
this.$modal.msgWarning('打印值为空,无法打印')
this.printRows([row])
},
printRows(rows) {
const targetRows = (rows || []).filter(row => row && row.barcodeNo)
if (!targetRows.length) {
this.$modal.msgWarning('请选择有打印值的条码')
return
}
const printNum = row.printNum || 1
const labelCount = targetRows.reduce((total, row) => total + Math.max(1, Number(row.printNum || 1)), 0)
const doPrint = () => this.renderBarcodeRows(targetRows)
if (labelCount >= 1000) {
this.$modal.confirm('本次将打印' + labelCount + '张条码,浏览器生成预览可能需要一些时间,是否继续?').then(doPrint).catch(() => {})
} else {
doPrint()
}
},
const doPrint = (fmt) => {
const barcodeType = (fmt && fmt.barcodeType) ? fmt.barcodeType : 'CODE128'
const lineWidth = (fmt && fmt.width) ? Number(fmt.width) : 2
const barHeight = (fmt && fmt.height) ? Number(fmt.height) : 60
const displayValue = fmt ? (fmt.showText === 1) : true
const fontSize = (fmt && fmt.fontSize) ? Number(fmt.fontSize) : 14
renderBarcodeRows(rows) {
const hidden = document.createElement('div')
hidden.style.cssText = 'position:fixed;left:-9999px;top:0;'
const svgs = []
rows.forEach(row => {
const fmt = this.getFormatById(row.formatId)
const barcodeType = (fmt && fmt.barcodeType) ? fmt.barcodeType : 'CODE128'
const lineWidth = (fmt && fmt.width) ? Number(fmt.width) : 2
const barHeight = (fmt && fmt.height) ? Number(fmt.height) : 60
const displayValue = fmt ? Number(fmt.showText) === 1 : true
const fontSize = (fmt && fmt.fontSize) ? Number(fmt.fontSize) : 14
const printNum = Math.max(1, Number(row.printNum || 1))
const hidden = document.createElement('div')
hidden.style.cssText = 'position:fixed;left:-9999px;top:0;'
const svgs = []
for (let i = 0; i < printNum; i++) {
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
hidden.appendChild(svg)
JsBarcode(svg, barcodeValue, {
JsBarcode(svg, row.barcodeNo, {
format: barcodeType,
width: lineWidth,
height: barHeight,
@@ -566,20 +477,25 @@ export default {
})
svgs.push(svg.outerHTML)
}
document.body.appendChild(hidden)
document.body.removeChild(hidden)
})
const printWindow = window.open('', '_blank', 'width=800,height=600')
printWindow.document.write(`<!DOCTYPE html>
document.body.appendChild(hidden)
document.body.removeChild(hidden)
this.printHtml(this.buildPrintHtml(svgs))
},
getFormatById(formatId) {
return this.formatOptions.find(item => String(item.id) === String(formatId)) || null
},
buildPrintHtml(svgs) {
return `<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>条形码打印</title>
<style>
@page {
size: auto;
margin: 3mm 6mm 3mm 6mm;
}
@page { size: auto; margin: 3mm 6mm 3mm 6mm; }
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: #fff;
@@ -588,7 +504,7 @@ export default {
flex-wrap: wrap;
justify-content: center;
align-items: center;
align-content: center;
align-content: flex-start;
min-height: 100vh;
}
.item {
@@ -596,6 +512,7 @@ export default {
vertical-align: middle;
text-align: center;
padding: 1mm 2mm;
page-break-inside: avoid;
}
.item svg {
display: block;
@@ -604,35 +521,29 @@ export default {
max-width: 65mm;
}
@media print {
body {
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
.item {
page-break-inside: avoid;
}
body { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
}
</style>
</head>
<body>
${svgs.map(s => '<div class="item">' + s + '</div>').join('\n ')}
<script>
window.onload = function() { window.print(); window.close(); }
<\/script>
</body>
</html>`)
printWindow.document.close()
}
</html>`
},
if (row.formatId) {
getBarcodeFormat(row.formatId).then(res => {
doPrint(res.data || null)
}).catch(() => {
doPrint(null)
})
} else {
doPrint(null)
}
printHtml(html) {
const iframe = document.createElement('iframe')
iframe.style.cssText = 'position:fixed;right:0;bottom:0;width:0;height:0;border:0;'
document.body.appendChild(iframe)
const doc = iframe.contentWindow.document
doc.open()
doc.write(html)
doc.close()
setTimeout(() => {
iframe.contentWindow.focus()
iframe.contentWindow.print()
setTimeout(() => document.body.removeChild(iframe), 1000)
}, 300)
}
}
}

View File

@@ -9,6 +9,15 @@
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="条码类型" prop="barcodeType">
<el-select v-model="queryParams.barcodeType" placeholder="请选择条码类型" clearable>
<el-option label="CODE128" value="CODE128" />
<el-option label="CODE39" value="CODE39" />
<el-option label="EAN13" value="EAN13" />
<el-option label="EAN8" value="EAN8" />
<el-option label="UPC" value="UPC" />
</el-select>
</el-form-item>
<el-form-item label="条码宽度" prop="width">
<el-input
v-model="queryParams.width"
@@ -158,24 +167,38 @@
<el-input v-model="form.formatName" placeholder="请输入格式名称" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="条码类型" prop="barcodeType">
<el-select v-model="form.barcodeType" placeholder="请选择条码类型" style="width:100%">
<el-option label="CODE128" value="CODE128" />
<el-option label="CODE39" value="CODE39" />
<el-option label="EAN13" value="EAN13" />
<el-option label="EAN8" value="EAN8" />
<el-option label="UPC" value="UPC" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="条码宽度" prop="width">
<el-input v-model="form.width" placeholder="请输入条码宽度" />
<el-input-number v-model="form.width" :min="1" :max="10" controls-position="right" style="width:100%" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="条码高度" prop="height">
<el-input v-model="form.height" placeholder="请输入条码高度" />
<el-input-number v-model="form.height" :min="20" :max="300" controls-position="right" style="width:100%" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="是否显示文本" prop="showText">
<el-input v-model="form.showText" placeholder="请输入是否显示文本" />
<el-radio-group v-model="form.showText">
<el-radio :label="1">显示</el-radio>
<el-radio :label="0">不显示</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="字体大小" prop="fontSize">
<el-input v-model="form.fontSize" placeholder="请输入字体大小" />
<el-input-number v-model="form.fontSize" :min="8" :max="72" controls-position="right" style="width:100%" />
</el-form-item>
</el-col>
<el-col :span="24">
@@ -245,6 +268,13 @@ export default {
form: {},
// 表单校验
rules: {
formatName: [{ required: true, message: '格式名称不能为空', trigger: 'blur' }],
barcodeType: [{ required: true, message: '条码类型不能为空', trigger: 'change' }],
width: [{ required: true, message: '条码宽度不能为空', trigger: 'blur' }],
height: [{ required: true, message: '条码高度不能为空', trigger: 'blur' }],
showText: [{ required: true, message: '请选择是否显示文本', trigger: 'change' }],
fontSize: [{ required: true, message: '字体大小不能为空', trigger: 'blur' }],
prefix: [{ required: true, message: '前缀不能为空', trigger: 'blur' }]
}
}
},
@@ -271,11 +301,11 @@ export default {
this.form = {
id: null,
formatName: null,
barcodeType: null,
width: null,
height: null,
showText: null,
fontSize: null,
barcodeType: 'CODE128',
width: 2,
height: 60,
showText: 1,
fontSize: 14,
prefix: null,
suffix: null,
remark: null,

View File

@@ -1,53 +1,26 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="84px">
<el-form-item label="物料编码" prop="productId">
<el-input v-model="queryParams.productId" placeholder="请输入物料编码" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="物料名称" prop="productName">
<el-input
v-model="queryParams.productName"
placeholder="请输入物料名称"
clearable
@keyup.enter.native="handleQuery"
/>
<el-input v-model="queryParams.productName" placeholder="请输入物料名称" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="分类" prop="parentId">
<el-input
v-model="queryParams.parentId"
placeholder="请输入分类"
clearable
@keyup.enter.native="handleQuery"
/>
<el-form-item label="项目号" prop="projectNo">
<el-input v-model="queryParams.projectNo" placeholder="请输入项目号" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="价格" prop="price">
<el-input
v-model="queryParams.price"
placeholder="请输入价格"
clearable
@keyup.enter.native="handleQuery"
/>
<el-form-item label="物料类别" prop="productType">
<el-input v-model="queryParams.productType" placeholder="请输入物料类别" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="图号" prop="cargoNo">
<el-input
v-model="queryParams.cargoNo"
placeholder="请输入图号"
clearable
@keyup.enter.native="handleQuery"
/>
<el-form-item label="图号" prop="drawingNo">
<el-input v-model="queryParams.drawingNo" placeholder="请输入图号" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="单位" prop="unit">
<el-input
v-model="queryParams.unit"
placeholder="请输入单位"
clearable
@keyup.enter.native="handleQuery"
/>
<el-form-item label="旧物料编码" prop="oldProductCode">
<el-input v-model="queryParams.oldProductCode" placeholder="请输入旧物料编码" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="规格" prop="spec">
<el-input
v-model="queryParams.spec"
placeholder="请输入规格"
clearable
@keyup.enter.native="handleQuery"
/>
<el-input v-model="queryParams.spec" placeholder="请输入规格" 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>
@@ -57,127 +30,105 @@
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAdd"
v-hasPermi="['bas:product:add']"
>新增</el-button>
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" v-hasPermi="['bas:product:add']">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-edit"
size="mini"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['bas:product:edit']"
>修改</el-button>
<el-button type="success" plain icon="el-icon-edit" size="mini" :disabled="single" @click="handleUpdate" v-hasPermi="['bas:product:edit']">修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['bas:product:remove']"
>删除</el-button>
<el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete" v-hasPermi="['bas:product:remove']">删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
size="mini"
@click="handleExport"
v-hasPermi="['bas:product:export']"
>导出</el-button>
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" v-hasPermi="['bas:product:export']">导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="productList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="物料编码" align="center" prop="productId" />
<el-table-column label="物料名称" align="center" prop="productName" show-overflow-tooltip/>
<el-table-column label="分类" align="center" prop="parentId" />
<el-table-column label="价格" align="center" prop="price" />
<el-table-column label="图号" align="center" prop="cargoNo" />
<el-table-column label="单位" align="center" prop="unit" />
<el-table-column label="规格" align="center" prop="spec" />
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<el-table-column label="物料编码" align="center" prop="productId" width="140" />
<el-table-column label="物料名称" align="center" prop="productName" min-width="180" show-overflow-tooltip />
<el-table-column label="项目号" align="center" prop="projectNo" width="120" />
<el-table-column label="物料类别" align="center" prop="productType" width="120" />
<el-table-column label="品牌" align="center" prop="brand" width="120" show-overflow-tooltip />
<el-table-column label="图号" align="center" prop="drawingNo" width="140" show-overflow-tooltip />
<el-table-column label="旧物料编码" align="center" prop="oldProductCode" width="140" show-overflow-tooltip />
<el-table-column label="单位" align="center" prop="unit" width="100" />
<el-table-column label="规格" align="center" prop="spec" min-width="160" show-overflow-tooltip />
<el-table-column label="材质名称" align="center" prop="productQualityName" width="140" show-overflow-tooltip />
<el-table-column label="备注" align="center" prop="remark" min-width="160" show-overflow-tooltip />
<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="['bas:product:edit']"
>修改</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-hasPermi="['bas:product:remove']"
>删除</el-button>
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" v-hasPermi="['bas:product:edit']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['bas:product:remove']">删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
<!-- 添加或修改物料信息对话框 -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
<el-row>
<el-col :span="24">
<el-dialog :title="title" :visible.sync="open" width="960px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="110px">
<el-divider content-position="left">基础信息</el-divider>
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="物料编码" prop="productId">
<el-input v-model="form.productId" :disabled="isUpdate" placeholder="请输入物料编码" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="物料名称" prop="productName">
<el-input v-model="form.productName" placeholder="请输入物料名称" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="分类" prop="parentId">
<el-input v-model="form.parentId" placeholder="请输入分类" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="价格" prop="price">
<el-input v-model="form.price" placeholder="请输入价格" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="图号" prop="cargoNo">
<el-input v-model="form.cargoNo" placeholder="请输入图号" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="单位" prop="unit">
<el-input v-model="form.unit" placeholder="请输入单位" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="规格" prop="spec">
<el-input v-model="form.spec" placeholder="请输入规格" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" placeholder="请输入备注" />
</el-form-item>
</el-col>
<el-col :span="12"><el-form-item label="分类" prop="parentId"><el-input v-model="form.parentId" placeholder="请输入分类" /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="物料类别" prop="productType"><el-input v-model="form.productType" placeholder="请输入物料类别" /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="项目号" prop="projectNo"><el-input v-model="form.projectNo" placeholder="请输入项目号" /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="图号" prop="drawingNo"><el-input v-model="form.drawingNo" placeholder="请输入图号" /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="图号(旧)" prop="cargoNo"><el-input v-model="form.cargoNo" placeholder="请输入图号(旧)" /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="代号" prop="partCode"><el-input v-model="form.partCode" placeholder="请输入代号" /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="规格" prop="spec"><el-input v-model="form.spec" placeholder="请输入规格" /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="单位" prop="unit"><el-input v-model="form.unit" placeholder="请输入单位" /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="价格" prop="price"><el-input v-model="form.price" placeholder="请输入价格" /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="品牌" prop="brand"><el-input v-model="form.brand" placeholder="请输入品牌" /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="助记码" prop="mnemonicCode"><el-input v-model="form.mnemonicCode" placeholder="请输入助记码" /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="旧物料编码" prop="oldProductCode"><el-input v-model="form.oldProductCode" placeholder="请输入旧物料编码" /></el-form-item></el-col>
<el-col :span="24"><el-form-item label="备注" prop="remark"><el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请输入备注" /></el-form-item></el-col>
</el-row>
<el-divider content-position="left">组织与分类</el-divider>
<el-row :gutter="16">
<el-col :span="12"><el-form-item label="创建组织编码" prop="createOrgCode"><el-input v-model="form.createOrgCode" placeholder="请输入创建组织编码" /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="创建组织名称" prop="createOrgName"><el-input v-model="form.createOrgName" placeholder="请输入创建组织名称" /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="使用组织编码" prop="useOrgCode"><el-input v-model="form.useOrgCode" placeholder="请输入使用组织编码" /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="使用组织名称" prop="useOrgName"><el-input v-model="form.useOrgName" placeholder="请输入使用组织名称" /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="存货类别编码" prop="inventoryCategoryCode"><el-input v-model="form.inventoryCategoryCode" placeholder="请输入存货类别编码" /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="存货类别名称" prop="inventoryCategoryName"><el-input v-model="form.inventoryCategoryName" placeholder="请输入存货类别名称" /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="材质编码" prop="productQualityCode"><el-input v-model="form.productQualityCode" placeholder="请输入材质编码" /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="材质名称" prop="productQualityName"><el-input v-model="form.productQualityName" placeholder="请输入材质名称" /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="物料属性" prop="erpClass"><el-input v-model="form.erpClass" placeholder="请输入物料属性" /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="特征件子项" prop="featureItem"><el-input v-model="form.featureItem" placeholder="请输入特征件子项" /></el-form-item></el-col>
<el-col :span="24"><el-form-item label="按批号匹配供需" prop="matchByLot"><el-input v-model="form.matchByLot" placeholder="请输入按批号匹配供需" /></el-form-item></el-col>
</el-row>
<el-divider content-position="left">业务属性</el-divider>
<el-row :gutter="16">
<el-col :span="12"><el-form-item label="采购备注" prop="purchaseRemark"><el-input v-model="form.purchaseRemark" placeholder="请输入采购备注" /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="套件" prop="isSuite"><el-select v-model="form.isSuite" clearable placeholder="请选择"><el-option v-for="item in boolOptions" :key="`suite-${item.value}`" :label="item.label" :value="item.value" /></el-select></el-form-item></el-col>
<el-col :span="8"><el-form-item label="允许采购" prop="allowPurchase"><el-select v-model="form.allowPurchase" clearable placeholder="请选择"><el-option v-for="item in boolOptions" :key="`purchase-${item.value}`" :label="item.label" :value="item.value" /></el-select></el-form-item></el-col>
<el-col :span="8"><el-form-item label="允许库存" prop="allowInventory"><el-select v-model="form.allowInventory" clearable placeholder="请选择"><el-option v-for="item in boolOptions" :key="`inventory-${item.value}`" :label="item.label" :value="item.value" /></el-select></el-form-item></el-col>
<el-col :span="8"><el-form-item label="允许委外" prop="allowSubcontract"><el-select v-model="form.allowSubcontract" clearable placeholder="请选择"><el-option v-for="item in boolOptions" :key="`sub-${item.value}`" :label="item.label" :value="item.value" /></el-select></el-form-item></el-col>
<el-col :span="8"><el-form-item label="允许销售" prop="allowSale"><el-select v-model="form.allowSale" clearable placeholder="请选择"><el-option v-for="item in boolOptions" :key="`sale-${item.value}`" :label="item.label" :value="item.value" /></el-select></el-form-item></el-col>
<el-col :span="8"><el-form-item label="允许生产" prop="allowProduce"><el-select v-model="form.allowProduce" clearable placeholder="请选择"><el-option v-for="item in boolOptions" :key="`produce-${item.value}`" :label="item.label" :value="item.value" /></el-select></el-form-item></el-col>
<el-col :span="8"><el-form-item label="允许转资产" prop="allowAsset"><el-select v-model="form.allowAsset" clearable placeholder="请选择"><el-option v-for="item in boolOptions" :key="`asset-${item.value}`" :label="item.label" :value="item.value" /></el-select></el-form-item></el-col>
</el-row>
<el-divider content-position="left">重量信息</el-divider>
<el-row :gutter="16">
<el-col :span="12"><el-form-item label="毛重" prop="grossWeight"><el-input v-model="form.grossWeight" placeholder="请输入毛重" /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="净重" prop="netWeight"><el-input v-model="form.netWeight" placeholder="请输入净重" /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="重量单位编码" prop="weightUnitCode"><el-input v-model="form.weightUnitCode" placeholder="请输入重量单位编码" /></el-form-item></el-col>
<el-col :span="12"><el-form-item label="重量单位名称" prop="weightUnitName"><el-input v-model="form.weightUnitName" placeholder="请输入重量单位名称" /></el-form-item></el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
@@ -195,39 +146,35 @@ export default {
name: "Product",
data() {
return {
// 遮罩层
loading: true,
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 物料信息表格数据
productList: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
productName: null,
parentId: null,
price: null,
cargoNo: null,
unit: null,
spec: null,
},
// 表单参数
isUpdate: false,
boolOptions: [
{ label: "是", value: 1 },
{ label: "否", value: 0 }
],
queryParams: {
pageNum: 1,
pageSize: 10,
productId: null,
productName: null,
projectNo: null,
productType: null,
drawingNo: null,
oldProductCode: null,
spec: null
},
form: {},
// 表单校验
rules: {
productId: [{ required: true, message: "物料编码不能为空", trigger: "blur" }],
productName: [{ required: true, message: "物料名称不能为空", trigger: "blur" }]
}
}
},
@@ -235,7 +182,6 @@ export default {
this.getList()
},
methods: {
/** 查询物料信息列表 */
getList() {
this.loading = true
listProduct(this.queryParams).then(response => {
@@ -244,12 +190,10 @@ export default {
this.loading = false
})
},
// 取消按钮
cancel() {
this.open = false
this.reset()
},
// 表单重置
reset() {
this.form = {
productId: null,
@@ -259,63 +203,82 @@ export default {
cargoNo: null,
unit: null,
spec: null,
remark: null
remark: null,
createOrgCode: null,
createOrgName: null,
useOrgCode: null,
useOrgName: null,
mnemonicCode: null,
oldProductCode: null,
matchByLot: null,
drawingNo: null,
partCode: null,
productType: null,
projectNo: null,
brand: null,
purchaseRemark: null,
productQualityCode: null,
productQualityName: null,
erpClass: null,
featureItem: null,
inventoryCategoryCode: null,
inventoryCategoryName: null,
allowPurchase: null,
allowInventory: null,
allowSubcontract: null,
allowSale: null,
allowProduce: null,
allowAsset: null,
grossWeight: null,
netWeight: null,
weightUnitCode: null,
weightUnitName: null,
isSuite: null
}
this.isUpdate = false
this.resetForm("form")
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm")
this.handleQuery()
},
// 多选框选中数据
handleSelectionChange(selection) {
this.ids = selection.map(item => item.productId)
this.single = selection.length !== 1
this.multiple = !selection.length
},
/** 新增按钮操作 */
handleAdd() {
this.reset()
this.isUpdate = false
this.open = true
this.title = "添加物料信息"
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset()
const productId = row.productId || this.ids
getProduct(productId).then(response => {
this.form = response.data
this.isUpdate = true
this.open = true
this.title = "修改物料信息"
})
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
if (this.form.productId != null) {
updateProduct(this.form).then(response => {
this.$modal.msgSuccess("修改成功")
this.open = false
this.getList()
})
} else {
addProduct(this.form).then(response => {
this.$modal.msgSuccess("新增成功")
this.open = false
this.getList()
})
}
const request = this.isUpdate ? updateProduct(this.form) : addProduct(this.form)
request.then(() => {
this.$modal.msgSuccess(this.isUpdate ? "修改成功" : "新增成功")
this.open = false
this.getList()
})
}
})
},
/** 删除按钮操作 */
handleDelete(row) {
const productIds = row.productId || this.ids
this.$modal.confirm('是否确认删除物料信息编号为"' + productIds + '"的数据项?').then(function() {
@@ -325,7 +288,6 @@ export default {
this.$modal.msgSuccess("删除成功")
}).catch(() => {})
},
/** 导出按钮操作 */
handleExport() {
this.download('bas/product/export', {
...this.queryParams

View File

@@ -0,0 +1,361 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="78px">
<el-form-item label="点位编码" prop="pointId">
<el-input v-model="queryParams.pointId" placeholder="请输入点位编码" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="点位名称" prop="pointName">
<el-input v-model="queryParams.pointName" placeholder="请输入点位名称" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="仓库编码" prop="warehouseId">
<el-select v-model="queryParams.warehouseId" placeholder="请选择仓库" clearable filterable>
<el-option v-for="item in warehouseOptions" :key="item.warehouseId" :label="item.warehouseName || item.warehouseId" :value="item.warehouseId" />
</el-select>
</el-form-item>
<el-form-item label="巷道编码" prop="roadwayId">
<el-select v-model="queryParams.roadwayId" placeholder="请选择巷道" clearable filterable>
<el-option v-for="item in queryRoadwayOptions" :key="item.roadwayId" :label="item.roadwayName || item.roadwayId" :value="item.roadwayId" />
</el-select>
</el-form-item>
<el-form-item label="点位类型" prop="pointType">
<el-select v-model="queryParams.pointType" placeholder="请选择点位类型" clearable>
<el-option label="入库" value="1" />
<el-option label="出库" value="2" />
<el-option label="入出库" value="3" />
</el-select>
</el-form-item>
<el-form-item label="点位大小" prop="pointSize">
<el-select v-model="queryParams.pointSize" placeholder="请选择点位大小" clearable>
<el-option label="大点位" value="1" />
<el-option label="小点位" value="2" />
</el-select>
</el-form-item>
<el-form-item label="托盘类型" prop="trayType">
<el-input v-model="queryParams.trayType" placeholder="请输入托盘类型" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="占用状态" prop="pointStatus">
<el-select v-model="queryParams.pointStatus" placeholder="请选择占用状态" clearable>
<el-option label="空闲" value="0" />
<el-option label="已分配" value="1" />
</el-select>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable>
<el-option label="启用" value="0" />
<el-option label="禁用" value="1" />
</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-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" v-hasPermi="['bas:stationpoint:add']">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="el-icon-edit" size="mini" :disabled="single" @click="handleUpdate" v-hasPermi="['bas:stationpoint:edit']">修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete" v-hasPermi="['bas:stationpoint:remove']">删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" v-hasPermi="['bas:stationpoint:export']">导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="stationPointList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="点位编码" align="center" prop="pointId" min-width="140" />
<el-table-column label="点位名称" align="center" prop="pointName" min-width="140" show-overflow-tooltip />
<el-table-column label="仓库编码" align="center" prop="warehouseId" width="120" />
<el-table-column label="巷道编码" align="center" prop="roadwayId" width="120" />
<el-table-column label="点位类型" align="center" prop="pointType" width="100">
<template slot-scope="scope">
<el-tag size="mini" :type="pointTypeTag(scope.row.pointType)">
{{ formatPointType(scope.row.pointType) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="点位大小" align="center" prop="pointSize" width="90">
<template slot-scope="scope">
{{ formatPointSize(scope.row.pointSize) }}
</template>
</el-table-column>
<el-table-column label="托盘类型" align="center" prop="trayType" width="110" show-overflow-tooltip />
<el-table-column label="占用状态" align="center" prop="pointStatus" width="90">
<template slot-scope="scope">
<el-tag size="mini" :type="scope.row.pointStatus === '1' ? 'danger' : 'success'">
{{ scope.row.pointStatus === '1' ? '已分配' : '空闲' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="status" width="90">
<template slot-scope="scope">
<el-tag size="mini" :type="scope.row.status === '0' ? 'success' : 'info'">
{{ scope.row.status === '0' ? '启用' : '禁用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="remark" min-width="160" show-overflow-tooltip />
<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="['bas:stationpoint:edit']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['bas:stationpoint:remove']">删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
<el-dialog :title="title" :visible.sync="open" width="520px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
<el-form-item label="点位编码" prop="pointId">
<el-input v-model="form.pointId" placeholder="请输入点位编码" />
</el-form-item>
<el-form-item label="点位名称" prop="pointName">
<el-input v-model="form.pointName" placeholder="请输入点位名称" />
</el-form-item>
<el-form-item label="仓库编码" prop="warehouseId">
<el-select v-model="form.warehouseId" placeholder="请选择仓库" filterable style="width:100%" @change="handleFormWarehouseChange">
<el-option v-for="item in warehouseOptions" :key="item.warehouseId" :label="item.warehouseName || item.warehouseId" :value="item.warehouseId" />
</el-select>
</el-form-item>
<el-form-item label="巷道编码" prop="roadwayId">
<el-select v-model="form.roadwayId" placeholder="请选择点位对应巷道" filterable style="width:100%" :disabled="!form.warehouseId">
<el-option v-for="item in formRoadwayOptions" :key="item.roadwayId" :label="item.roadwayName || item.roadwayId" :value="item.roadwayId" />
</el-select>
</el-form-item>
<el-form-item label="点位类型" prop="pointType">
<el-select v-model="form.pointType" placeholder="请选择点位类型">
<el-option label="入库" value="1" />
<el-option label="出库" value="2" />
<el-option label="入出库" value="3" />
</el-select>
</el-form-item>
<el-form-item label="点位大小" prop="pointSize">
<el-select v-model="form.pointSize" placeholder="请选择点位大小" style="width:100%">
<el-option label="大点位" value="1" />
<el-option label="小点位" value="2" />
</el-select>
</el-form-item>
<el-form-item label="托盘类型" prop="trayType">
<el-input v-model="form.trayType" placeholder="请输入允许托盘类型,如 A 或 A,B" />
</el-form-item>
<el-form-item label="占用状态" prop="pointStatus">
<el-radio-group v-model="form.pointStatus" disabled>
<el-radio label="0">空闲</el-radio>
<el-radio label="1">已分配</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="form.status">
<el-radio label="0">启用</el-radio>
<el-radio label="1">禁用</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入备注" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm">确 定</el-button>
<el-button @click="cancel">取 消</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { listStationPoint, getStationPoint, delStationPoint, addStationPoint, updateStationPoint } from '@/api/bas/stationpoint'
import { listWarehouse } from '@/api/bas/warehouse'
import { listRoadway } from '@/api/bas/roadway'
export default {
name: 'StationPoint',
data() {
return {
loading: true,
ids: [],
single: true,
multiple: true,
showSearch: true,
total: 0,
stationPointList: [],
warehouseOptions: [],
roadwayOptions: [],
title: '',
open: false,
queryParams: {
pageNum: 1,
pageSize: 10,
pointId: null,
pointName: null,
warehouseId: null,
roadwayId: null,
pointType: null,
pointSize: null,
trayType: null,
pointStatus: null,
status: null
},
form: {},
rules: {
pointId: [{ required: true, message: '点位编码不能为空', trigger: 'blur' }],
warehouseId: [{ required: true, message: '仓库编码不能为空', trigger: 'blur' }],
roadwayId: [{ required: true, message: '巷道编码不能为空', trigger: 'blur' }],
pointType: [{ required: true, message: '点位类型不能为空', trigger: 'change' }],
pointSize: [{ required: true, message: '点位大小不能为空', trigger: 'change' }],
trayType: [{ required: true, message: '托盘类型不能为空', trigger: 'blur' }],
status: [{ required: true, message: '状态不能为空', trigger: 'change' }]
}
}
},
created() {
this.getList()
this.getWarehouseOptions()
this.getRoadwayOptions()
},
computed: {
queryRoadwayOptions() {
if (!this.queryParams.warehouseId) {
return this.roadwayOptions
}
return this.roadwayOptions.filter(item => item.warehouseId === this.queryParams.warehouseId)
},
formRoadwayOptions() {
if (!this.form.warehouseId) {
return []
}
return this.roadwayOptions.filter(item => item.warehouseId === this.form.warehouseId)
}
},
methods: {
formatPointType(type) {
if (type === '1') return '入库'
if (type === '2') return '出库'
if (type === '3') return '入出库'
return type || '-'
},
pointTypeTag(type) {
if (type === '1') return 'success'
if (type === '2') return 'warning'
if (type === '3') return 'primary'
return 'info'
},
formatPointSize(size) {
if (size === '1') return '大点位'
if (size === '2') return '小点位'
return size || '-'
},
getList() {
this.loading = true
listStationPoint(this.queryParams).then(response => {
this.stationPointList = response.rows
this.total = response.total
this.loading = false
})
},
getWarehouseOptions() {
listWarehouse({ pageNum: 1, pageSize: 999 }).then(response => {
this.warehouseOptions = response.rows || []
})
},
getRoadwayOptions() {
listRoadway({ pageNum: 1, pageSize: 999 }).then(response => {
this.roadwayOptions = response.rows || []
})
},
handleFormWarehouseChange() {
if (!this.formRoadwayOptions.some(item => item.roadwayId === this.form.roadwayId)) {
this.form.roadwayId = null
}
},
cancel() {
this.open = false
this.reset()
},
reset() {
this.form = {
id: null,
pointId: null,
pointName: null,
warehouseId: null,
roadwayId: null,
pointType: '1',
pointSize: '1',
trayType: null,
pointStatus: '0',
status: '0',
remark: null
}
this.resetForm('form')
},
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
resetQuery() {
this.resetForm('queryForm')
this.handleQuery()
},
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id)
this.single = selection.length !== 1
this.multiple = !selection.length
},
handleAdd() {
this.reset()
this.open = true
this.title = '新增点位配置'
},
handleUpdate(row) {
this.reset()
const id = row.id || this.ids
getStationPoint(id).then(response => {
this.form = response.data
if (!this.form.pointStatus) {
this.form.pointStatus = '0'
}
this.open = true
this.title = '修改点位配置'
})
},
submitForm() {
this.$refs.form.validate(valid => {
if (!valid) return
if (this.form.id != null) {
updateStationPoint(this.form).then(() => {
this.$modal.msgSuccess('修改成功')
this.open = false
this.getList()
})
} else {
addStationPoint(this.form).then(() => {
this.$modal.msgSuccess('新增成功')
this.open = false
this.getList()
})
}
})
},
handleDelete(row) {
const ids = row.id || this.ids
this.$modal.confirm('是否确认删除点位配置编号为"' + ids + '"的数据项?').then(function() {
return delStationPoint(ids)
}).then(() => {
this.getList()
this.$modal.msgSuccess('删除成功')
}).catch(() => {})
},
handleExport() {
this.download('bas/stationpoint/export', {
...this.queryParams
}, `stationpoint_${new Date().getTime()}.xlsx`)
}
}
}
</script>

View File

@@ -210,7 +210,7 @@
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="stockList" @selection-change="handleSelectionChange">
<el-table v-loading="loading" :data="stockList" highlight-current-row @row-click="handleStockRowClick" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="库存id" align="center" prop="stockId" width="160"/>
<el-table-column label="货位id" align="center" prop="cargoSpaceId" />
@@ -219,18 +219,12 @@
<dict-tag :options="dict.type.bas_stock_stock_status" :value="scope.row.stockStatus"/>
</template>
</el-table-column>
<el-table-column label="物料状态" align="center" prop="productStatus">
<template slot-scope="scope">
<dict-tag :options="dict.type.bas_stock_product_status" :value="scope.row.productStatus"/>
</template>
</el-table-column>
<el-table-column label="托盘条码" align="center" prop="trayCode" />
<el-table-column label="批次号" align="center" prop="lotNo" />
<el-table-column label="物料编码" align="center" prop="productId" />
<el-table-column label="物料名称" align="center" prop="productName" show-overflow-tooltip/>
<el-table-column label="库存数量" align="center" prop="quantity" />
<el-table-column label="出库数量" align="center" prop="outQuantity" />
<el-table-column label="仓库名称" align="center" prop="warehouseName" />
<el-table-column label="明细数" align="center" prop="detailCount" width="80" />
<el-table-column label="总库存" align="center" prop="quantity" width="100" />
<el-table-column label="已出库" align="center" prop="outQuantity" width="100" />
<el-table-column label="可用库存" align="center" prop="availableQuantity" width="100" />
<el-table-column label="入库时间" align="center" prop="inDate" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.inDate, '{y}-{m}-{d}') }}</span>
@@ -242,8 +236,6 @@
<!-- </template>-->
<!-- </el-table-column>-->
<el-table-column label="作业ID" align="center" prop="jobId" width="160"/>
<el-table-column label="子作业" align="center" prop="jobDetailId" width="160"/>
<el-table-column label="入库子单" align="center" prop="stockinDetailId" width="160"/>
<el-table-column label="入库主单" align="center" prop="stockinId" width="160"/>
<el-table-column label="入库类型" align="center" prop="inType">
<template slot-scope="scope">
@@ -281,6 +273,68 @@
@pagination="getList"
/>
<el-divider content-position="left">库存物料明细</el-divider>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
:disabled="!currentStock"
@click="handleDetailAdd"
v-hasPermi="['bas:stock:add']"
>新增明细</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-edit"
size="mini"
:disabled="detailSingle"
@click="handleDetailUpdate"
v-hasPermi="['bas:stock:edit']"
>修改明细</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="detailMultiple"
@click="handleDetailDelete"
v-hasPermi="['bas:stock:remove']"
>删除明细</el-button>
</el-col>
</el-row>
<el-table v-loading="detailLoading" :data="stockDetailList" @selection-change="handleDetailSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="明细id" align="center" prop="detailId" width="160" />
<el-table-column label="物料状态" align="center" prop="productStatus">
<template slot-scope="scope">
<dict-tag :options="dict.type.bas_stock_product_status" :value="scope.row.productStatus"/>
</template>
</el-table-column>
<el-table-column label="批次号" align="center" prop="lotNo" />
<el-table-column label="物料编码" align="center" prop="productId" />
<el-table-column label="物料名称" align="center" prop="productName" show-overflow-tooltip />
<el-table-column label="库存数量" align="center" prop="quantity" />
<el-table-column label="出库数量" align="center" prop="outQuantity" />
<el-table-column label="子作业" align="center" prop="jobDetailId" width="160" />
<el-table-column label="入库子单" align="center" prop="stockinDetailId" width="160" />
</el-table>
<pagination
v-show="detailTotal>0"
:total="detailTotal"
:page.sync="detailQueryParams.pageNum"
:limit.sync="detailQueryParams.pageSize"
@pagination="getDetailList"
/>
<!-- 添加或修改库存信息对话框 -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
@@ -290,16 +344,6 @@
<el-input v-model="form.cargoSpaceId" placeholder="请输入货位id" />
</el-form-item>
</el-col>-->
<el-col :span="24">
<el-form-item label="物料编码" prop="productId">
<el-input v-model="form.productId" placeholder="请输入物料编码" readonly/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="物料名称" prop="productName" >
<el-input v-model="form.productName" placeholder="请输入物料名称" readonly/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="库存状态" prop="stockStatus">
<el-select v-model="form.stockStatus" placeholder="请选择库存状态">
@@ -312,29 +356,6 @@
</el-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="物料状态" prop="productStatus">
<el-select v-model="form.productStatus" placeholder="请选择物料状态">
<el-option
v-for="dict in dict.type.bas_stock_product_status"
:key="dict.value"
:label="dict.label"
:value="dict.value"
></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="批次号" prop="lotNo">
<el-input v-model="form.lotNo" placeholder="请输入批次号" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="库存数量" prop="quantity">
<el-input v-model="form.quantity" placeholder="请输入库存数量" />
</el-form-item>
</el-col>
<!-- <el-col :span="24">
<el-form-item label="入库时间" prop="inDate">
<el-date-picker clearable
@@ -445,11 +466,58 @@
<el-button @click="cancel">取 消</el-button>
</div>
</el-dialog>
<!-- 添加或修改库存物料明细对话框 -->
<el-dialog :title="detailTitle" :visible.sync="detailOpen" width="500px" append-to-body>
<el-form ref="detailForm" :model="detailForm" :rules="detailRules" label-width="100px">
<el-form-item label="库存id" prop="stockId">
<el-input v-model="detailForm.stockId" readonly />
</el-form-item>
<el-form-item label="物料状态" prop="productStatus">
<el-select v-model="detailForm.productStatus" placeholder="请选择物料状态">
<el-option
v-for="dict in dict.type.bas_stock_product_status"
:key="dict.value"
:label="dict.label"
:value="dict.value"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="批次号" prop="lotNo">
<el-input v-model="detailForm.lotNo" placeholder="请输入批次号" />
</el-form-item>
<el-form-item label="物料编码" prop="productId">
<el-input v-model="detailForm.productId" placeholder="请输入物料编码" />
</el-form-item>
<el-form-item label="物料名称" prop="productName">
<el-input v-model="detailForm.productName" placeholder="请输入物料名称" />
</el-form-item>
<el-form-item label="库存数量" prop="quantity">
<el-input v-model="detailForm.quantity" placeholder="请输入库存数量" />
</el-form-item>
<el-form-item label="出库数量" prop="outQuantity">
<el-input v-model="detailForm.outQuantity" placeholder="请输入出库数量" />
</el-form-item>
<el-form-item label="子作业id" prop="jobDetailId">
<el-input v-model="detailForm.jobDetailId" placeholder="请输入子作业id" />
</el-form-item>
<el-form-item label="入库子单id" prop="stockinDetailId">
<el-input v-model="detailForm.stockinDetailId" placeholder="请输入入库子单id" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="detailForm.remark" type="textarea" placeholder="请输入内容" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitDetailForm">确 定</el-button>
<el-button @click="detailOpen = false">取 消</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { listStock, getStock, delStock, addStock, updateStock } from "@/api/bas/stock"
import { listStock, getStock, delStock, addStock, updateStock, listStockDetail, getStockDetail, delStockDetail, addStockDetail, updateStockDetail } from "@/api/bas/stock"
export default {
name: "Stock",
@@ -470,10 +538,28 @@ export default {
total: 0,
// 库存信息表格数据
stockList: [],
// 当前选中的库存主表记录
currentStock: null,
// 库存物料明细遮罩层
detailLoading: false,
// 库存物料明细表格数据
stockDetailList: [],
// 库存物料明细总条数
detailTotal: 0,
// 明细选中数组
detailIds: [],
// 明细非单个禁用
detailSingle: true,
// 明细非多个禁用
detailMultiple: true,
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 明细弹出层标题
detailTitle: "",
// 是否显示明细弹出层
detailOpen: false,
// 供应商名称时间范围
daterangeInDate: [],
// 供应商名称时间范围
@@ -503,10 +589,33 @@ export default {
deptName: null,
suplierName: null
},
// 明细查询参数
detailQueryParams: {
pageNum: 1,
pageSize: 10,
stockId: null,
productStatus: null,
lotNo: null,
productId: null,
productName: null,
jobDetailId: null,
stockinDetailId: null
},
// 表单参数
form: {},
// 明细表单参数
detailForm: {},
// 表单校验
rules: {
},
// 明细表单校验
detailRules: {
productId: [
{ required: true, message: "物料编码不能为空", trigger: "blur" }
],
quantity: [
{ required: true, message: "库存数量不能为空", trigger: "blur" }
]
}
}
},
@@ -530,6 +639,29 @@ export default {
this.stockList = response.rows
this.total = response.total
this.loading = false
if (this.stockList.length > 0) {
const current = this.currentStock ? this.stockList.find(item => item.stockId === this.currentStock.stockId) : null
this.handleStockRowClick(current || this.stockList[0])
} else {
this.currentStock = null
this.stockDetailList = []
this.detailTotal = 0
}
})
},
/** 查询库存物料明细列表 */
getDetailList() {
if (!this.currentStock || !this.currentStock.stockId) {
this.stockDetailList = []
this.detailTotal = 0
return
}
this.detailLoading = true
this.detailQueryParams.stockId = this.currentStock.stockId
listStockDetail(this.detailQueryParams).then(response => {
this.stockDetailList = response.rows
this.detailTotal = response.total
this.detailLoading = false
})
},
// 取消按钮
@@ -568,6 +700,23 @@ export default {
}
this.resetForm("form")
},
// 明细表单重置
resetDetail() {
this.detailForm = {
detailId: null,
stockId: this.currentStock ? this.currentStock.stockId : null,
productStatus: null,
lotNo: null,
productId: null,
productName: null,
quantity: null,
outQuantity: null,
jobDetailId: null,
stockinDetailId: null,
remark: null
}
this.resetForm("detailForm")
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1
@@ -586,6 +735,21 @@ export default {
this.single = selection.length !== 1
this.multiple = !selection.length
},
// 点击库存主表行后加载物料明细
handleStockRowClick(row) {
if (!row) {
return
}
this.currentStock = row
this.detailQueryParams.pageNum = 1
this.getDetailList()
},
// 明细多选框选中数据
handleDetailSelectionChange(selection) {
this.detailIds = selection.map(item => item.detailId)
this.detailSingle = selection.length !== 1
this.detailMultiple = !selection.length
},
/** 新增按钮操作 */
handleAdd() {
this.reset()
@@ -595,7 +759,7 @@ export default {
/** 修改按钮操作 */
handleUpdate(row) {
this.reset()
const stockId = row.stockId || this.ids
const stockId = row && row.stockId ? row.stockId : this.ids
getStock(stockId).then(response => {
this.form = response.data
this.open = true
@@ -622,9 +786,63 @@ export default {
}
})
},
/** 新增明细按钮操作 */
handleDetailAdd() {
if (!this.currentStock) {
this.$modal.msgWarning("请先选择库存主表记录")
return
}
this.resetDetail()
this.detailOpen = true
this.detailTitle = "添加库存物料明细"
},
/** 修改明细按钮操作 */
handleDetailUpdate(row) {
this.resetDetail()
const detailId = row && row.detailId ? row.detailId : this.detailIds
getStockDetail(detailId).then(response => {
this.detailForm = response.data
this.detailOpen = true
this.detailTitle = "修改库存物料明细"
})
},
/** 明细提交按钮 */
submitDetailForm() {
this.$refs["detailForm"].validate(valid => {
if (valid) {
this.detailForm.stockId = this.currentStock.stockId
if (this.detailForm.detailId != null) {
updateStockDetail(this.detailForm).then(response => {
this.$modal.msgSuccess("修改成功")
this.detailOpen = false
this.getDetailList()
this.getList()
})
} else {
addStockDetail(this.detailForm).then(response => {
this.$modal.msgSuccess("新增成功")
this.detailOpen = false
this.getDetailList()
this.getList()
})
}
}
})
},
/** 明细删除按钮操作 */
handleDetailDelete(row) {
const detailIds = row && row.detailId ? row.detailId : this.detailIds
this.$modal.confirm('是否确认删除库存物料明细编号为"' + detailIds + '"的数据项?').then(function() {
return delStockDetail(detailIds)
}).then(() => {
this.getDetailList()
this.getList()
this.$modal.msgSuccess("删除成功")
}).catch(() => {})
},
/** 删除按钮操作 */
handleDelete(row) {
const stockIds = row.stockId || this.ids
const stockIds = row && row.stockId ? row.stockId : this.ids
this.$modal.confirm('是否确认删除库存信息编号为"' + stockIds + '"的数据项?').then(function() {
return delStock(stockIds)
}).then(() => {

View File

@@ -0,0 +1,252 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="88px">
<el-form-item label="规则名称" prop="ruleName">
<el-input v-model="queryParams.ruleName" placeholder="请输入规则名称" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="托盘类型" prop="trayType">
<el-input v-model="queryParams.trayType" placeholder="请输入托盘类型" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="允许仓库" prop="allowedWarehouses">
<el-select v-model="queryParams.allowedWarehouses" placeholder="请选择仓库" clearable filterable>
<el-option v-for="item in warehouseOptions" :key="item.warehouseId" :label="item.warehouseName" :value="item.warehouseId" />
</el-select>
</el-form-item>
<el-form-item label="货位类型" prop="allowedCargoType">
<el-input v-model="queryParams.allowedCargoType" placeholder="请输入货位类型" 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>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" v-hasPermi="['bas:trayRule:add']">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="el-icon-edit" size="mini" :disabled="single" @click="handleUpdate" v-hasPermi="['bas:trayRule:edit']">修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete" v-hasPermi="['bas:trayRule:remove']">删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" v-hasPermi="['bas:trayRule:export']">导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="trayRuleList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="规则名称" align="center" prop="ruleName" min-width="140" show-overflow-tooltip />
<el-table-column label="托盘类型" align="center" prop="trayType" width="100" />
<el-table-column label="尺寸" align="center" width="120">
<template slot-scope="scope">{{ scope.row.trayLength }} x {{ scope.row.trayWidth }}</template>
</el-table-column>
<el-table-column label="允许仓库" align="center" prop="allowedWarehouseNames" min-width="180" show-overflow-tooltip />
<el-table-column label="货位类型" align="center" prop="allowedCargoType" width="120" />
<el-table-column label="货位类型名称" align="center" prop="allowedCargoTypeName" min-width="140" show-overflow-tooltip />
<el-table-column label="备注" align="center" prop="remark" min-width="140" show-overflow-tooltip />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="160">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" v-hasPermi="['bas:trayRule:edit']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['bas:trayRule:remove']">删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
<el-dialog :title="title" :visible.sync="open" width="620px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="110px">
<el-form-item label="规则名称" prop="ruleName">
<el-input v-model="form.ruleName" placeholder="请输入规则名称" />
</el-form-item>
<el-form-item label="托盘类型" prop="trayType">
<el-input v-model="form.trayType" maxlength="10" placeholder="请输入托盘条码首字母,如 A" />
</el-form-item>
<el-row>
<el-col :span="12">
<el-form-item label="托盘长度" prop="trayLength">
<el-input-number v-model="form.trayLength" :min="1" :max="999999" controls-position="right" style="width:100%" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="托盘宽度" prop="trayWidth">
<el-input-number v-model="form.trayWidth" :min="1" :max="999999" controls-position="right" style="width:100%" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="允许仓库" prop="allowedWarehouseArray">
<el-select v-model="form.allowedWarehouseArray" multiple filterable clearable placeholder="请选择允许入库仓库" style="width:100%">
<el-option v-for="item in warehouseOptions" :key="item.warehouseId" :label="item.warehouseName" :value="item.warehouseId" />
</el-select>
</el-form-item>
<el-row>
<el-col :span="12">
<el-form-item label="货位类型" prop="allowedCargoType">
<el-input v-model="form.allowedCargoType" placeholder="请输入货位类型" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="货位类型名称" prop="allowedCargoTypeName">
<el-input v-model="form.allowedCargoTypeName" placeholder="请输入货位类型名称" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入备注" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm">确 定</el-button>
<el-button @click="cancel">取 消</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { listTrayRule, getTrayRule, delTrayRule, addTrayRule, updateTrayRule } from '@/api/bas/trayRule'
import { listWarehouse } from '@/api/bas/warehouse'
export default {
name: 'TrayRule',
data() {
return {
loading: true,
ids: [],
single: true,
multiple: true,
showSearch: true,
total: 0,
trayRuleList: [],
warehouseOptions: [],
title: '',
open: false,
form: {},
queryParams: {
pageNum: 1,
pageSize: 10,
ruleName: null,
trayType: null,
allowedWarehouses: null,
allowedCargoType: null
},
rules: {
ruleName: [{ required: true, message: '规则名称不能为空', trigger: 'blur' }],
trayType: [{ required: true, message: '托盘类型不能为空', trigger: 'blur' }],
trayLength: [{ required: true, message: '托盘长度不能为空', trigger: 'blur' }],
trayWidth: [{ required: true, message: '托盘宽度不能为空', trigger: 'blur' }],
allowedWarehouseArray: [{ required: true, type: 'array', message: '允许仓库不能为空', trigger: 'change' }],
allowedCargoType: [{ required: true, message: '货位类型不能为空', trigger: 'blur' }]
}
}
},
created() {
this.getList()
this.getWarehouseOptions()
},
methods: {
getList() {
this.loading = true
listTrayRule(this.queryParams).then(response => {
this.trayRuleList = response.rows
this.total = response.total
this.loading = false
})
},
getWarehouseOptions() {
listWarehouse({ pageNum: 1, pageSize: 999 }).then(response => {
this.warehouseOptions = response.rows || []
})
},
cancel() {
this.open = false
this.reset()
},
reset() {
this.form = {
id: null,
ruleName: null,
trayType: null,
trayLength: 1,
trayWidth: 1,
allowedWarehouses: null,
allowedWarehouseNames: null,
allowedWarehouseArray: [],
allowedCargoType: null,
allowedCargoTypeName: null,
remark: null
}
this.resetForm('form')
},
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
resetQuery() {
this.resetForm('queryForm')
this.handleQuery()
},
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id)
this.single = selection.length !== 1
this.multiple = !selection.length
},
handleAdd() {
this.reset()
this.open = true
this.title = '添加托盘入库规则'
},
handleUpdate(row) {
this.reset()
const id = row.id || this.ids
getTrayRule(id).then(response => {
this.form = response.data
this.form.allowedWarehouseArray = this.form.allowedWarehouses ? this.form.allowedWarehouses.split(',').filter(Boolean) : []
this.open = true
this.title = '修改托盘入库规则'
})
},
submitForm() {
this.$refs.form.validate(valid => {
if (!valid) return
const data = { ...this.form }
const selectedWarehouses = this.warehouseOptions.filter(item => data.allowedWarehouseArray.indexOf(item.warehouseId) !== -1)
data.allowedWarehouses = data.allowedWarehouseArray.join(',')
data.allowedWarehouseNames = selectedWarehouses.map(item => item.warehouseName || item.warehouseId).join(',')
delete data.allowedWarehouseArray
if (data.id != null) {
updateTrayRule(data).then(() => {
this.$modal.msgSuccess('修改成功')
this.open = false
this.getList()
})
} else {
addTrayRule(data).then(() => {
this.$modal.msgSuccess('新增成功')
this.open = false
this.getList()
})
}
})
},
handleDelete(row) {
const ids = row.id || this.ids
this.$modal.confirm('是否确认删除托盘入库规则编号为"' + ids + '"的数据项?').then(function() {
return delTrayRule(ids)
}).then(() => {
this.getList()
this.$modal.msgSuccess('删除成功')
}).catch(() => {})
},
handleExport() {
this.download('bas/trayRule/export', {
...this.queryParams
}, `trayRule_${new Date().getTime()}.xlsx`)
}
}
}
</script>

View File

@@ -95,6 +95,66 @@
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="单据编号" prop="documentN">
<el-input v-model="mainForm.documentN" placeholder="请输入单据编号" clearable />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="单据日期" prop="documentDat">
<el-date-picker
v-model="mainForm.documentDat"
type="date"
value-format="yyyy-MM-dd"
placeholder="请选择单据日期"
style="width:100%"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="收料组织" prop="receivingOr">
<el-input v-model="mainForm.receivingOr" placeholder="请输入收料组织" clearable />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="采购组织" prop="purchasingOr">
<el-input v-model="mainForm.purchasingOr" placeholder="请输入采购组织" clearable />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="收料部门" prop="receivingDep">
<el-input v-model="mainForm.receivingDep" placeholder="请输入收料部门" clearable />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="采购部门" prop="purchasingDep">
<el-input v-model="mainForm.purchasingDep" placeholder="请输入采购部门" clearable />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="仓管员" prop="handle">
<el-input v-model="mainForm.handle" placeholder="请输入仓管员" clearable />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="采购员" prop="creato">
<el-input v-model="mainForm.creato" placeholder="请输入采购员" clearable />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="审核人" prop="reviewe">
<el-input v-model="mainForm.reviewe" placeholder="请输入审核人" clearable />
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
@@ -277,6 +337,16 @@ export default {
inType: null,
stockSource: null,
erpBill: null,
documentN: null,
documentDat: null,
counterpart: null,
receivingOr: null,
purchasingOr: null,
receivingDep: null,
purchasingDep: null,
handle: null,
creato: null,
reviewe: null,
startDate: null,
warehouseId: null,
warehouseName: null,
@@ -345,6 +415,7 @@ export default {
onSupplierChange(val) {
const s = this.supplierOptions.find(o => o.supplierId === val)
this.mainForm.supplierName = s ? s.supplierName : null
this.mainForm.counterpart = s ? s.supplierName : null
},
// ===== 物料弹框 =====
@@ -396,10 +467,19 @@ export default {
this.detailLines.push({
productId: p.productId,
productName: p.productName,
itemCod: p.productId,
itemNam: p.productName,
spec: p.spec || '',
specMode: p.spec || '',
unit: p.unit || '',
uni: p.unit || '',
lotNo: '',
quantity: 1,
warehous: this.mainForm.warehouseName || '',
locatio: '',
inventoryStatus: '',
sourceDocN: '',
remar: '',
remark: ''
})
}
@@ -438,14 +518,26 @@ export default {
// 构造保存数据(主单 + 明细)
const saveData = {
...this.mainForm,
tota: this.totalQuantity,
details: this.detailLines.map(line => ({
lineN: line.lineN || null,
productId: line.productId,
productName: line.productName,
itemCod: line.itemCod || line.productId,
itemNam: line.itemNam || line.productName,
specMode: line.specMode || line.spec || null,
uni: line.uni || line.unit || null,
lotNo: line.lotNo || null,
quantity: line.quantity,
scanNum: 0,
assignNum: 0,
remark: line.remark || null
actualQt: line.actualQt || null,
warehous: line.warehous || this.mainForm.warehouseName || null,
locatio: line.locatio || null,
inventoryStatus: line.inventoryStatus || null,
sourceDocN: line.sourceDocN || null,
remar: line.remar || line.remark || null,
remark: line.remark || line.remar || null
}))
}
const res = await saveStockinWithDetails(saveData)

View File

@@ -125,7 +125,7 @@
<!-- <el-table-column label="重量" align="center" prop="weight" />-->
<el-table-column label="创建时间" align="center" prop="createTime" width="160">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createDate, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column label="开始时间" align="center" prop="startTime" width="160">
@@ -313,7 +313,7 @@ export default {
handleRowClick(row) {
this.currentJob = row
this.detailQueryParams.jobId = row.jobId
this.detailQueryParams.status = this.queryParams.status
this.detailQueryParams.status = row.status
this.detailQueryParams.pageNum = 1
this.getJobDetailList()
},

View File

@@ -149,6 +149,17 @@
>完成单据
</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-printer"
size="mini"
:disabled="single"
@click="handlePrintBarcode"
>打印单据码
</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
@@ -246,6 +257,37 @@
/>
</div>
<!-- 打印入库子单二维码 -->
<el-dialog title="打印入库单据码" :visible.sync="printOpen" width="760px" append-to-body>
<el-alert
v-if="printStockin"
:title="'主单:' + printStockin.stockinId + ',请选择需要打印二维码的子单'"
type="info"
show-icon
:closable="false"
style="margin-bottom: 12px;"
/>
<el-table
ref="printDetailTable"
v-loading="printLoading"
:data="printDetailList"
border
size="small"
max-height="420"
@selection-change="handlePrintSelectionChange"
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="子单ID" align="center" prop="stockinDetailId" min-width="190" />
<el-table-column label="物料编码" align="center" prop="productId" width="150" />
<el-table-column label="物料名称" align="center" prop="productName" min-width="160" show-overflow-tooltip />
<el-table-column label="计划数量" align="center" prop="quantity" width="90" />
</el-table>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="handleConfirmPrint">确认打印</el-button>
<el-button @click="printOpen = false">取 消</el-button>
</div>
</el-dialog>
<!-- 添加或修改入库单据主信息对话框 -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
@@ -383,6 +425,7 @@ import {
auditStockin,
finishStockin
} from "@/api/in/stockin"
import QRCode from "qrcode"
export default {
name: "Stockin",
@@ -478,7 +521,13 @@ export default {
pageSize: 10,
stockinId: null,
status: null
}
},
// ---- 打印子单二维码 ----
printOpen: false,
printLoading: false,
printStockin: null,
printDetailList: [],
printSelection: []
}
},
created() {
@@ -666,6 +715,119 @@ export default {
this.getList()
}).catch(() => {
})
},
/** 打开子单二维码打印弹窗 */
handlePrintBarcode() {
const stockinId = this.ids[0]
const row = this.stockinList.find(item => item.stockinId === stockinId)
this.printStockin = row || { stockinId: stockinId }
this.printOpen = true
this.printLoading = true
this.printDetailList = []
this.printSelection = []
listStockinDetail({ stockinId: stockinId, pageNum: 1, pageSize: 999 }).then(response => {
this.printDetailList = response.rows || []
this.printLoading = false
this.$nextTick(() => {
if (!this.$refs.printDetailTable) return
this.printDetailList.forEach(row => {
this.$refs.printDetailTable.toggleRowSelection(row, true)
})
})
}).catch(() => {
this.printLoading = false
})
},
/** 记录弹窗中选择的待打印子单 */
handlePrintSelectionChange(selection) {
this.printSelection = selection
},
/** 生成选中子单的二维码并调用浏览器打印 */
async handleConfirmPrint() {
if (!this.printSelection.length) {
this.$modal.msgWarning('请选择需要打印的子单')
return
}
this.printLoading = true
try {
const printItems = await Promise.all(this.printSelection.map(async item => {
const qrCodeUrl = await QRCode.toDataURL(String(item.stockinDetailId || ''), {
errorCorrectionLevel: 'M',
margin: 1,
width: 180
})
return {
...item,
qrCodeUrl
}
}))
this.printByIframe(printItems)
this.printOpen = false
} catch (error) {
this.$modal.msgError('生成二维码失败,请稍后重试')
} finally {
this.printLoading = false
}
},
printByIframe(printItems) {
const title = this.printStockin ? ('入库单据码-' + this.printStockin.stockinId) : '入库单据码'
const labelHtml = printItems.map(item => `
<div class="label-card">
<img class="qr" src="${item.qrCodeUrl}" alt="${this.escapeHtml(item.stockinDetailId || '')}" />
<div class="label-info">
<div class="label-title">入库子单二维码</div>
<div class="detail-id">${this.escapeHtml(item.stockinDetailId || '')}</div>
<div class="meta">物料编码:${this.escapeHtml(item.productId || '-')}</div>
<div class="meta">物料名称:${this.escapeHtml(item.productName || '-')}</div>
<div class="meta">计划数量:${this.escapeHtml(item.quantity == null ? '-' : item.quantity)}</div>
</div>
</div>
`).join('')
const printFrame = document.createElement('iframe')
printFrame.style.position = 'fixed'
printFrame.style.right = '0'
printFrame.style.bottom = '0'
printFrame.style.width = '0'
printFrame.style.height = '0'
printFrame.style.border = '0'
document.body.appendChild(printFrame)
const printDocument = printFrame.contentWindow.document
printDocument.open()
printDocument.write(`
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>${title}</title>
<style>
* { box-sizing: border-box; }
body { margin: 0; padding: 12px; font-family: Arial, "Microsoft YaHei", sans-serif; color: #000; }
.label-card { width: 100%; min-height: 190px; padding: 12px; margin: 0 0 10px 0; border: 1px solid #000; display: flex; align-items: center; page-break-inside: avoid; break-inside: avoid; }
.qr { width: 165px; height: 165px; flex: 0 0 165px; margin-right: 18px; }
.label-info { flex: 1; min-width: 0; }
.label-title { font-size: 16px; font-weight: 700; margin-bottom: 10px; }
.detail-id { font-size: 14px; font-weight: 700; word-break: break-all; margin-bottom: 10px; }
.meta { font-size: 13px; line-height: 1.8; word-break: break-all; }
@media print { body { padding: 0; } .label-card { margin: 0 0 8px 0; } }
</style>
</head>
<body>${labelHtml}</body>
</html>
`)
printDocument.close()
setTimeout(() => {
printFrame.contentWindow.focus()
printFrame.contentWindow.print()
document.body.removeChild(printFrame)
}, 300)
},
escapeHtml(value) {
return String(value).replace(/[&<>"]/g, match => ({
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;'
}[match]))
}
}
}

View File

@@ -1,7 +1,5 @@
<template>
<div class="app-container create-stockout">
<!-- ===== 页面标题栏 ===== -->
<div class="page-header">
<span class="page-title"><i class="el-icon-document-add"></i> 新建出库单据</span>
<div class="header-actions">
@@ -11,14 +9,12 @@
</div>
</div>
<!-- ===== 主单基础信息 ===== -->
<el-card shadow="never" class="section-card">
<div slot="header" class="card-header">
<i class="el-icon-tickets"></i> 基本信息
</div>
<el-form ref="mainForm" :model="mainForm" :rules="mainRules" label-width="100px" size="small">
<el-row :gutter="24">
<!-- 仓库 -->
<el-col :span="8">
<el-form-item label="仓库" prop="warehouseId">
<el-select
@@ -39,7 +35,6 @@
</el-form-item>
</el-col>
<!-- 客户 -->
<el-col :span="8">
<el-form-item label="客户" prop="customerId">
<el-select
@@ -60,7 +55,6 @@
</el-form-item>
</el-col>
<!-- 出库类型 -->
<el-col :span="8">
<el-form-item label="出库类型" prop="outType">
<el-select v-model="mainForm.outType" placeholder="请选择出库类型" style="width:100%">
@@ -74,7 +68,6 @@
</el-form-item>
</el-col>
<!-- 单据来源 -->
<el-col :span="8">
<el-form-item label="单据来源" prop="stockSource">
<el-select v-model="mainForm.stockSource" placeholder="请选择单据来源" style="width:100%">
@@ -88,18 +81,87 @@
</el-form-item>
</el-col>
<!-- 上位单号 -->
<el-col :span="8">
<el-form-item label="上位单号" prop="erpBill">
<el-input v-model="mainForm.erpBill" placeholder="请输入上位单号(选填)" clearable />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="单据编号" prop="documentN">
<el-input v-model="mainForm.documentN" placeholder="请输入单据编号" clearable />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="单据日期" prop="documentDat">
<el-date-picker
v-model="mainForm.documentDat"
type="date"
value-format="yyyy-MM-dd"
placeholder="请选择单据日期"
style="width:100%"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="项目号/项目名称" prop="projectNam">
<el-input v-model="mainForm.projectNam" placeholder="请输入项目号/项目名称" clearable />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="发货人" prop="handle">
<el-input v-model="mainForm.handle" placeholder="请输入发货人" clearable />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="制单人" prop="creato">
<el-input v-model="mainForm.creato" placeholder="请输入制单人" clearable />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="物流公司" prop="logisticsCompany">
<el-input v-model="mainForm.logisticsCompany" placeholder="请输入物流公司" clearable />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="物流单号" prop="logisticsN">
<el-input v-model="mainForm.logisticsN" placeholder="请输入物流单号" clearable />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="审批人" prop="reviewe">
<el-input v-model="mainForm.reviewe" placeholder="请输入审批人" clearable />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="收货人" prop="receive">
<el-input v-model="mainForm.receive" placeholder="请输入收货人" clearable />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="收货地址" prop="receiverA">
<el-input v-model="mainForm.receiverA" placeholder="请输入收货地址" clearable />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="收货人电话" prop="receiverPhon">
<el-input v-model="mainForm.receiverPhon" placeholder="请输入收货人电话" clearable />
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
<!-- ===== 物料明细 ===== -->
<el-card shadow="never" class="section-card" style="margin-top:16px;">
<div slot="header" class="card-header">
<span><i class="el-icon-goods"></i> 物料明细</span>
@@ -122,13 +184,13 @@
>
<el-table-column type="index" label="序号" width="55" align="center" />
<el-table-column label="库存ID" prop="stockId" align="center" width="120" />
<el-table-column label="物料编码" prop="productId" align="center" width="130" />
<el-table-column label="物料名称" prop="productName" align="center" min-width="150" show-overflow-tooltip />
<el-table-column label="批次" prop="lotNo" align="center" width="130" />
<el-table-column label="货位" prop="cargoSpaceId" align="center" width="100" />
<el-table-column label="托盘条码" prop="trayCode" align="center" width="130" show-overflow-tooltip />
<el-table-column label="物料编码" prop="productId" align="center" width="140" />
<el-table-column label="物料名称" prop="productName" align="center" min-width="160" show-overflow-tooltip />
<el-table-column label="批次" prop="lotNo" align="center" width="140" />
<el-table-column label="货位" prop="cargoSpaceId" align="center" width="120" />
<el-table-column label="托盘条码" prop="trayCode" align="center" width="140" show-overflow-tooltip />
<el-table-column label="可用库存" prop="stockQuantity" align="center" width="100" />
<el-table-column label="出库数量" align="center" width="140">
<el-table-column label="计划出库数量" align="center" width="170">
<template slot-scope="scope">
<el-input-number
v-model="scope.row.outQuantity"
@@ -136,12 +198,11 @@
:max="scope.row.stockQuantity || 999999"
size="mini"
controls-position="right"
style="width:120px"
@change="val => onOutQtyChange(val, scope.$index)"
style="width:130px"
/>
</template>
</el-table-column>
<el-table-column label="备注" align="center" min-width="120">
<el-table-column label="备注" align="center" min-width="140">
<template slot-scope="scope">
<el-input v-model="scope.row.remark" size="mini" placeholder="备注(选填)" clearable />
</template>
@@ -158,7 +219,6 @@
</template>
</el-table-column>
<!-- 空状态 -->
<template slot="empty">
<div style="padding:40px 0; color:#c0c4cc;">
<i class="el-icon-shopping-cart-2" style="font-size:36px;display:block;margin-bottom:8px;"></i>
@@ -168,14 +228,12 @@
</template>
</el-table>
<!-- 汇总行 -->
<div v-if="detailLines.length > 0" class="summary-bar">
共 <strong>{{ detailLines.length }}</strong> 种物料,
合计出库 <strong>{{ totalQuantity }}</strong> 件
合计计划出库 <strong>{{ totalQuantity }}</strong> 件
</div>
</el-card>
<!-- ===== 库存选择弹框 ===== -->
<el-dialog
title="选择库存物料"
:visible.sync="stockDialogVisible"
@@ -184,22 +242,13 @@
top="6vh"
@open="onStockDialogOpen"
>
<!-- 搜索栏 -->
<div class="stock-search">
<el-input
v-model="stockQuery.warehouseId"
placeholder="仓库ID"
size="small"
clearable
style="width:140px;margin-right:8px;"
@keyup.enter.native="searchStock"
/>
<el-input
v-model="stockQuery.productName"
placeholder="物料名称"
size="small"
clearable
style="width:160px;margin-right:8px;"
style="width:180px;margin-right:8px;"
@keyup.enter.native="searchStock"
/>
<el-input
@@ -207,7 +256,7 @@
placeholder="物料编码"
size="small"
clearable
style="width:140px;margin-right:8px;"
style="width:160px;margin-right:8px;"
@keyup.enter.native="searchStock"
/>
<el-input
@@ -215,14 +264,13 @@
placeholder="批次号"
size="small"
clearable
style="width:130px;margin-right:8px;"
style="width:140px;margin-right:8px;"
@keyup.enter.native="searchStock"
/>
<el-button type="primary" size="small" icon="el-icon-search" @click="searchStock">搜索</el-button>
<el-button size="small" icon="el-icon-refresh" @click="resetStockSearch">重置</el-button>
</div>
<!-- 库存表格(多选) -->
<el-table
ref="stockTable"
v-loading="stockLoading"
@@ -235,16 +283,15 @@
@selection-change="onStockSelectionChange"
>
<el-table-column type="selection" width="45" align="center" :selectable="isStockSelectable" />
<el-table-column label="仓库" prop="warehouseName" align="center" width="120" show-overflow-tooltip />
<el-table-column label="货位" prop="cargoSpaceId" align="center" width="100" />
<el-table-column label="物料编码" prop="productId" align="center" width="120" />
<el-table-column label="物料名称" prop="productName" align="center" min-width="130" show-overflow-tooltip />
<el-table-column label="批次" prop="lotNo" align="center" width="120" />
<el-table-column label="托盘条码" prop="trayCode" align="center" width="120" show-overflow-tooltip />
<el-table-column label="可用库存" prop="quantity" align="center" width="90" />
<el-table-column label="仓库" prop="warehouseName" align="center" width="130" show-overflow-tooltip />
<el-table-column label="货位" prop="cargoSpaceId" align="center" width="120" />
<el-table-column label="物料编码" prop="productId" align="center" width="130" />
<el-table-column label="物料名称" prop="productName" align="center" min-width="160" show-overflow-tooltip />
<el-table-column label="批次" prop="lotNo" align="center" width="140" />
<el-table-column label="托盘条码" prop="trayCode" align="center" width="140" show-overflow-tooltip />
<el-table-column label="可用库存" prop="availableQuantity" align="center" width="100" />
</el-table>
<!-- 弹框分页 -->
<pagination
v-show="stockTotal > 0"
:total="stockTotal"
@@ -259,12 +306,9 @@
已选 <strong style="color:#409eff;">{{ selectedStocks.length }}</strong> 条库存记录
</span>
<el-button @click="stockDialogVisible = false">取 消</el-button>
<el-button type="primary" @click="confirmStocks" :disabled="selectedStocks.length === 0">
确认添加
</el-button>
<el-button type="primary" @click="confirmStocks" :disabled="selectedStocks.length === 0">确认添加</el-button>
</div>
</el-dialog>
</div>
</template>
@@ -272,7 +316,7 @@
import { saveWithDetails } from '@/api/out/stockout'
import { listWarehouse } from '@/api/bas/warehouse'
import { listCustomer } from '@/api/bas/customer'
import { listStock } from '@/api/bas/stock'
import { listStockDetail } from '@/api/bas/stock'
export default {
name: 'CreateStockout',
@@ -280,40 +324,44 @@ export default {
data() {
return {
saving: false,
// ---------- 主单表单 ----------
mainForm: {
outType: null,
stockSource: null,
erpBill: null,
documentN: null,
documentDat: null,
projectNam: null,
counterpart: null,
handle: null,
creato: null,
logisticsCompany: null,
logisticsN: null,
reviewe: null,
receive: null,
receiverA: null,
receiverPhon: null,
warehouseId: null,
warehouseName: null,
customerId: null,
customerName: null,
status: '0' // 默认待出库
status: '0'
},
mainRules: {
warehouseId: [{ required: true, message: '请选择仓库', trigger: 'change' }],
customerId: [{ required: true, message: '请选择客户', trigger: 'change' }],
outType: [{ required: true, message: '请选择出库类型', trigger: 'change' }],
stockSource: [{ required: true, message: '请选择单据来源', trigger: 'change' }]
warehouseId: [{ required: true, message: '请选择仓库', trigger: 'change' }],
customerId: [{ required: true, message: '请选择客户', trigger: 'change' }],
outType: [{ required: true, message: '请选择出库类型', trigger: 'change' }],
stockSource: [{ required: true, message: '请选择单据来源', trigger: 'change' }]
},
// ---------- 下拉数据 ----------
warehouseOptions: [],
customerOptions: [],
// ---------- 明细行 ----------
detailLines: [],
// ---------- 库存弹框 ----------
stockDialogVisible: false,
stockLoading: false,
stockList: [],
stockTotal: 0,
stockQuery: {
pageNum: 1,
pageSize: 8,
pageSize: 10,
warehouseId: null,
productId: null,
productName: null,
@@ -322,22 +370,18 @@ export default {
selectedStocks: []
}
},
computed: {
totalQuantity() {
return this.detailLines.reduce((sum, l) => sum + (Number(l.outQuantity) || 0), 0)
return this.detailLines.reduce((sum, line) => sum + (Number(line.outQuantity) || 0), 0)
}
},
created() {
this.fetchWarehouseOptions()
this.fetchCustomerOptions()
},
methods: {
// ===== 基础数据加载 =====
fetchWarehouseOptions() {
listWarehouse({ pageSize: 200,warehouseType: 1 }).then(res => {
listWarehouse({ pageSize: 200 }).then(res => {
this.warehouseOptions = res.rows || []
})
},
@@ -346,24 +390,17 @@ export default {
this.customerOptions = res.rows || []
})
},
// ===== 仓库/客户联动 =====
onWarehouseChange(val) {
const w = this.warehouseOptions.find(o => o.warehouseId === val)
this.mainForm.warehouseName = w ? w.warehouseName : null
const warehouse = this.warehouseOptions.find(item => item.warehouseId === val)
this.mainForm.warehouseName = warehouse ? warehouse.warehouseName : null
this.stockQuery.warehouseId = val || null
this.detailLines = []
},
onCustomerChange(val) {
const c = this.customerOptions.find(o => o.customerId === val)
this.mainForm.customerName = c ? c.customerName : null
const customer = this.customerOptions.find(item => item.customerId === val)
this.mainForm.customerName = customer ? customer.customerName : null
this.mainForm.counterpart = customer ? customer.customerName : null
},
// ===== 出库数量变化 =====
onOutQtyChange(val, index) {
// 可在此扩展超限提示等逻辑
},
// ===== 库存弹框 =====
openStockDialog() {
this.stockDialogVisible = true
},
@@ -384,89 +421,104 @@ export default {
},
fetchStockList() {
this.stockLoading = true
listStock(this.stockQuery).then(res => {
listStockDetail(this.stockQuery).then(res => {
this.stockList = res.rows || []
this.stockTotal = res.total || 0
this.stockLoading = false
// 回显已选中行
this.$nextTick(() => {
if (this.$refs.stockTable) {
this.stockList.forEach(row => {
const already = this.detailLines.some(l => l.stockId === row.stockId)
if (already) {
this.$refs.stockTable.toggleRowSelection(row, true)
}
})
}
})
}).catch(() => { this.stockLoading = false })
}).catch(() => {
this.stockLoading = false
})
},
onStockSelectionChange(selection) {
this.selectedStocks = selection
},
// 已在明细行中的库存记录置为不可重复勾选
isStockSelectable(row) {
return !this.detailLines.some(l => l.stockId === row.stockId)
return !this.detailLines.some(line => line.detailId === row.detailId)
},
confirmStocks() {
this.selectedStocks.forEach(s => {
if (!this.detailLines.some(l => l.stockId === s.stockId)) {
this.selectedStocks.forEach(stock => {
if (!this.detailLines.some(line => line.detailId === stock.detailId)) {
const stockQuantity = Number(stock.availableQuantity != null ? stock.availableQuantity : stock.quantity) || 0
this.detailLines.push({
stockId: s.stockId, // 库存ID
productId: s.productId, // 物料编码
productName: s.productName, // 物料名称
lotNo: s.lotNo || '', // 批次
cargoSpaceId: s.cargoSpaceId || '', // 货位
trayCode: s.trayCode || '', // 托盘条码
stockQuantity: s.quantity, // 可用库存(原始)
outQuantity: 1, // 出库数量默认1
remark: ''
detailId: stock.detailId,
stockId: stock.stockId,
productId: stock.productId,
productName: stock.productName,
itemCod: stock.productId,
itemNam: stock.productName,
lotNo: stock.lotNo || '',
stockQuantity: stockQuantity,
outQuantity: 1,
cargoSpaceId: stock.cargoSpaceId || '',
trayCode: stock.trayCode || '',
specMode: stock.spec || '',
uni: stock.unit || '',
warehous: this.mainForm.warehouseName || '',
locatio: stock.cargoSpaceId || '',
inventoryStatus: stock.status || '',
sourceDocN: this.mainForm.erpBill || '',
remar: '',
remark: ''
})
}
})
this.stockDialogVisible = false
this.selectedStocks = []
},
// ===== 明细行操作 =====
removeLine(index) {
this.detailLines.splice(index, 1)
},
// ===== 保存 =====
handleSave() {
// 1. 主单校验
this.$refs.mainForm.validate(valid => {
if (!valid) return
// 2. 明细非空校验
if (this.detailLines.length === 0) {
this.$modal.msgWarning('请至少添加一条库存明细')
this.$modal.msgWarning('请至少选择一条库存明细')
return
}
// 3. 数量校验
const invalidLine = this.detailLines.find(l => !l.outQuantity || l.outQuantity <= 0)
const invalidLine = this.detailLines.find(line => !line.outQuantity || line.outQuantity <= 0)
if (invalidLine) {
this.$modal.msgWarning(`物料【${invalidLine.productName}】的出库数量必须大于 0`)
return
}
const exceedLine = this.detailLines.find(line => Number(line.outQuantity) > Number(line.stockQuantity || 0))
if (exceedLine) {
this.$modal.msgWarning(`物料【${exceedLine.productName}】的出库数量不能大于可用库存`)
return
}
this.doSave()
})
},
async doSave() {
this.saving = true
try {
// 构造保存数据(主单 + 明细)
const saveData = {
...this.mainForm,
details: this.detailLines.map(line => ({
stockId: line.stockId,
productId: line.productId,
counterpart: this.mainForm.counterpart || this.mainForm.customerName,
warehous: this.mainForm.warehouseName,
printCoun: 0,
tota: this.totalQuantity,
details: this.detailLines.map((line, index) => ({
lineN: index + 1,
stockId: line.stockId,
stockDetailId: line.detailId,
productId: line.productId,
productName: line.productName,
lotNo: line.lotNo || null,
cargoSpaceId: line.cargoSpaceId || null,
trayCode: line.trayCode || null,
quantity: line.outQuantity, // 出库数量
remark: line.remark || null
itemCod: line.itemCod || line.productId,
itemNam: line.itemNam || line.productName,
lotNo: line.lotNo || null,
quantity: line.outQuantity,
allotNum: line.outQuantity,
assignNum: 0,
specMode: line.specMode || null,
uni: line.uni || null,
actualQt: null,
warehous: line.warehous || this.mainForm.warehouseName || null,
locatio: line.locatio || line.cargoSpaceId || null,
inventoryStatus: line.inventoryStatus || null,
sourceDocN: line.sourceDocN || this.mainForm.erpBill || null,
remar: line.remar || line.remark || null,
remark: line.remark || line.remar || null,
status: '0'
}))
}
const res = await saveWithDetails(saveData)
@@ -475,16 +527,14 @@ export default {
return
}
this.$modal.msgSuccess('出库单据创建成功!')
// 跳回列表页
this.$router.push({ path: '/out/stockout' })
} catch (e) {
} catch (error) {
this.$modal.msgError('保存失败,请检查网络或联系管理员')
console.error(e)
console.error(error)
} finally {
this.saving = false
}
}
}
}
</script>
@@ -494,27 +544,28 @@ export default {
padding: 20px;
}
/* 页面标题栏 */
.page-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16px;
}
.page-title {
font-size: 18px;
font-weight: 600;
color: #303133;
}
.header-actions {
display: flex;
gap: 8px;
}
/* 分区卡片 */
.section-card {
border-radius: 4px;
}
.card-header {
display: flex;
align-items: center;
@@ -523,31 +574,24 @@ export default {
font-weight: 600;
color: #303133;
}
.card-header i {
margin-right: 6px;
color: #409eff;
}
/* 库存搜索栏 */
.summary-bar {
margin-top: 12px;
padding: 10px 12px;
background: #f5f7fa;
border-radius: 4px;
color: #606266;
}
.stock-search {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 4px;
}
/* 汇总栏 */
.summary-bar {
margin-top: 10px;
padding: 8px 12px;
background: #f0f9ff;
border-radius: 4px;
font-size: 13px;
color: #606266;
border-left: 3px solid #409eff;
}
.summary-bar strong {
color: #409eff;
margin: 0 2px;
}
</style>

View File

@@ -98,6 +98,17 @@
v-hasPermi="['out:job:complete']"
>手动完成</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-upload2"
size="mini"
:disabled="single"
@click="handlePushWcs"
v-hasPermi="['out:job:edit']"
>推送WCS</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
@@ -202,7 +213,7 @@
</template>
<script>
import { listJob, cancelJob, completeJob } from "@/api/out/job"
import { listJob, cancelJob, completeJob, pushJobToWcs } from "@/api/out/job"
import { listJobdetail } from "@/api/out/jobdetail"
export default {
@@ -309,6 +320,16 @@ export default {
this.getList()
}).catch(() => {})
},
/** 推送 WCS 按钮操作 */
handlePushWcs() {
const jobId = this.ids[0]
this.$modal.confirm('是否确认推送选中的出库作业到WCS?').then(() => {
return pushJobToWcs(jobId)
}).then(() => {
this.$modal.msgSuccess("推送成功")
this.getList()
}).catch(() => {})
},
/** 点击主作业行 —— 联动刷新子作业 */
handleRowClick(row) {
this.currentJob = row

View File

@@ -250,6 +250,7 @@
<el-table-column label="批次" align="center" prop="lotNo" width="150"/>
<el-table-column label="物料编码" align="center" prop="productId" width="150"/>
<el-table-column label="物料名称" align="center" prop="productName" min-width="150" show-overflow-tooltip/>
<el-table-column label="库存明细" align="center" prop="stockDetailId" width="160" show-overflow-tooltip/>
<el-table-column label="计划数量" align="center" prop="quantity" width="100"/>
<el-table-column label="扫码数量" align="center" prop="allotNum" width="100"/>
<el-table-column label="实际数量" align="center" prop="assignNum" width="100"/>
@@ -288,12 +289,13 @@
>
<el-table-column type="selection" width="55" align="center"/>
<el-table-column label="库存ID" prop="stockId" align="center"/>
<el-table-column label="明细ID" prop="detailId" align="center" width="160" show-overflow-tooltip/>
<el-table-column label="物料编码" prop="productId" align="center"/>
<el-table-column label="物料名称" prop="productName" align="center"/>
<el-table-column label="批次" prop="lotNo" align="center"/>
<el-table-column label="货位" prop="cargoSpaceName" align="center"/>
<el-table-column label="托盘号" prop="trayCode" align="center"/>
<el-table-column label="可用数量" prop="quantity" align="center"/>
<el-table-column label="可用数量" prop="availableQuantity" align="center"/>
</el-table>
<div slot="footer" class="dialog-footer">
@@ -314,7 +316,7 @@ import {
manualAssignStockout
} from "@/api/out/stockout"
import { listStockoutdetail } from "@/api/out/stockoutdetail"
import { listStock } from "@/api/bas/stock"
import { listStockDetail } from "@/api/bas/stock"
export default {
name: "Stockout",
@@ -445,8 +447,12 @@ export default {
const detail = this.detailList.find(item => item.stockoutDetailId === this.detailIds[0])
this.assignDialogVisible = true
this.stockLoading = true
// 根据物料编码查询可用库存
listStock({ productId: detail.productId }).then(res => {
// 根据物料编码查询可用库存明细
listStockDetail({
productId: detail.productId,
warehouseId: this.currentStockout ? this.currentStockout.warehouseId : null,
stockStatus: '1'
}).then(res => {
this.stockList = res.rows
this.stockLoading = false
})
@@ -463,7 +469,7 @@ export default {
this.$modal.msgWarning("请选择要出库的库存")
return
}
const stockIdList = this.selectedStockList.map(item => item.stockId)
const stockIdList = Array.from(new Set(this.selectedStockList.map(item => item.detailId || item.stockDetailId || item.stockId)))
const detailId = this.detailIds[0]
this.$modal.confirm("确认选中库存进行分配?").then(() => {

View File

@@ -220,7 +220,7 @@
<el-table-column label="物料名称" prop="productName" align="center" min-width="130" show-overflow-tooltip />
<el-table-column label="批次" prop="lotNo" align="center" width="120" />
<el-table-column label="托盘条码" prop="trayCode" align="center" width="120" show-overflow-tooltip />
<el-table-column label="可用库存" prop="quantity" align="center" width="90" />
<el-table-column label="可用库存" prop="availableQuantity" align="center" width="90" />
</el-table>
<!-- 弹框分页 -->
@@ -250,7 +250,7 @@
<script>
import { saveWithDetails } from '@/api/pd/stockpd'
import { listWarehouse } from '@/api/bas/warehouse'
import { listStock } from '@/api/bas/stock'
import { listStockDetail } from '@/api/bas/stock'
export default {
name: 'CreateStockpd',
@@ -348,7 +348,7 @@ export default {
},
fetchStockList() {
this.stockLoading = true
listStock(this.stockQuery).then(res => {
listStockDetail(this.stockQuery).then(res => {
this.stockList = res.rows || []
this.stockTotal = res.total || 0
this.stockLoading = false
@@ -356,7 +356,7 @@ export default {
this.$nextTick(() => {
if (this.$refs.stockTable) {
this.stockList.forEach(row => {
const already = this.detailLines.some(l => l.stockId === row.stockId)
const already = this.detailLines.some(l => l.detailId === row.detailId)
if (already) {
this.$refs.stockTable.toggleRowSelection(row, true)
}
@@ -370,19 +370,20 @@ export default {
},
// 已在明细行中的库存记录置为不可重复勾选
isStockSelectable(row) {
return !this.detailLines.some(l => l.stockId === row.stockId)
return !this.detailLines.some(l => l.detailId === row.detailId)
},
confirmStocks() {
this.selectedStocks.forEach(s => {
if (!this.detailLines.some(l => l.stockId === s.stockId)) {
if (!this.detailLines.some(l => l.detailId === s.detailId)) {
this.detailLines.push({
detailId: s.detailId,
stockId: s.stockId,
productId: s.productId,
productName: s.productName,
lotNo: s.lotNo || '',
cargoSpaceId: s.cargoSpaceId || '',
trayCode: s.trayCode || '',
stockQuantity: s.quantity,
stockQuantity: Number(s.availableQuantity != null ? s.availableQuantity : s.quantity) || 0,
pdQuantity: 1, // 盘点数量默认1
remark: ''
})