Browse Source

Merge remote-tracking branch 'origin/master'

master
常熟吴彦祖 1 month ago
parent
commit
4101a2ac86
  1. 2
      src/api/mr/mr.js
  2. 5
      src/views/modules/mr-issue/index.vue
  3. 132
      src/views/modules/mr-issue/mrPickingDetail.vue
  4. 522
      src/views/modules/other-transaction/receiveFromTransit.vue

2
src/api/mr/mr.js

@ -23,3 +23,5 @@ export const scanMaterialLabelNotInStock = data => createAPI(`/pda/mr/scanMateri
export const getInventoryPart = data => createAPI(`/pda/mr/getInventoryPart`, 'post', data)
export const confirmMrUnIssue = data => createAPI(`/pda/mr/confirmMrUnIssue`, 'post', data)
export const printLabelCommon = data => createAPI('/label/setting/printLabelCommon','post',data)

5
src/views/modules/mr-issue/index.vue

@ -38,13 +38,14 @@ export default {
to: "mrPicking",
disabled: false,
},
{
/* MR没有退料 */
/* {
icon: "records",
label: "MR退料",
iconClass: "qualified",
to: "mrPickingReturn",
disabled: false,
},
}, */
],
};
},

132
src/views/modules/mr-issue/mrPickingDetail.vue

@ -23,11 +23,14 @@
</div>
</div>
<!-- 主体滚动区域 -->
<div class="scrollable-content">
<!-- MR信息卡片 -->
<div class="work-order-list" v-if="mrInfo.orderNo">
<div class="material-card">
<div class="card-title">
<span class="title-label">MR号{{ mrInfo.orderNo }} &nbsp;&nbsp; 行号{{ mrInfo.lineNo }} &nbsp;&nbsp; lineItemNo:{{ mrInfo.lineItemNo }}</span>
<span class="title-label">MR号{{ mrInfo.orderNo }} &nbsp;&nbsp; 行号{{ mrInfo.lineNo }} &nbsp;&nbsp;
lineItemNo:{{ mrInfo.lineItemNo }}</span>
</div>
<!-- 物料描述单独一行 -->
@ -72,8 +75,9 @@
<div class="col-qty">标签数量</div>
</div>
<div class="list-body">
<div v-for="(label, index) in labelList" :key="label.id" class="list-item">
<div class="col-no">{{ labelList.length - index }}</div>
<div class="col-no">{{ index+1 }}</div>
<div class="col-label">{{ label.labelCode }}</div>
<div class="col-batch">{{ label.batchNo }}</div>
<div class="col-qty" @click="openEditDialog(label, index)">
@ -87,6 +91,8 @@
<p>暂无扫描标签</p>
</div>
</div>
</div>
</div>
<!-- 发料汇总 -->
<!-- <div class="issue-summary" v-if="labelList.length > 0">
@ -102,12 +108,12 @@
<!-- 底部操作按钮 -->
<div class="bottom-actions">
<button class="action-btn primary" @click="confirmIssue">
<el-button class="action-btn primary" @click="confirmIssue" :loading="issueloading">
确认发料
</button>
<button class="action-btn secondary" style="margin-left: 10px;" @click="cancelIssue">
</el-button>
<el-button class="action-btn secondary" style="margin-left: 10px;" @click="cancelIssue">
取消
</button>
</el-button>
</div>
<!-- 编辑标签弹框 -->
@ -121,40 +127,22 @@
<div class="modal-body">
<div class="form-group">
<label class="form-label">物料标签</label>
<el-input
v-model="editForm.labelCode"
disabled
class="form-input"
/>
<el-input v-model="editForm.labelCode" disabled class="form-input" />
</div>
<div class="form-group">
<label class="form-label">批次号</label>
<el-input
v-model="editForm.batchNo"
disabled
class="form-input"
/>
<el-input v-model="editForm.batchNo" disabled class="form-input" />
</div>
<div class="form-group">
<label class="form-label">库位 <span class="required">*</span></label>
<el-input
v-model="editForm.locationId"
placeholder="请输入库位"
class="form-input"
/>
<el-input v-model="editForm.locationId" placeholder="请输入库位" class="form-input" />
</div>
<div class="form-group">
<label class="form-label">发料数量 <span class="required">*</span></label>
<el-input
v-model="editForm.quantity"
type="number"
:min="0"
placeholder="请输入发料数量"
class="form-input"
/>
<el-input v-model="editForm.quantity" type="number" :min="0" placeholder="请输入发料数量" class="form-input" />
</div>
</div>
@ -168,7 +156,12 @@
</template>
<script>
import { scanMaterialLabel, confirmMrIssue } from '@/api/mr/mr'
import {
scanMaterialLabel,
confirmMrIssue,
printLabelCommon,
} from '@/api/mr/mr'
import { Loading } from 'element-ui'
import moment from 'moment'
export default {
@ -188,9 +181,10 @@ export default {
labelCode: '',
batchNo: '',
locationId: '',
quantity: 0
quantity: 0,
},
editIndex: -1, //
issueloading: false,
}
},
computed: {
@ -325,20 +319,19 @@ export default {
engChgLevel: label.engChgLevel || '1',
})),
}
this.$confirm(
`确认发料 ${this.totalIssueQty} ${this.mrInfo.unitMeas || 'pcs'}`,
'确认发料',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(() => {
this.issueloading = true
confirmMrIssue(params)
.then(({ data }) => {
if (data && data.code === 0) {
if (data.unitIds && data.unitIds.length > 0) {
let printLabelType
if (this.mrInfo.partNo && this.mrInfo.partNo.startsWith('80')) {
printLabelType = '库存成品标签'
} else {
printLabelType = 'BIL标签'
}
this.printViaServer(data.unitIds, printLabelType)
}
this.$message.success('发料成功')
sessionStorage.setItem('mrPicking_shouldRestore', 'true')
this.$router.back()
@ -350,11 +343,43 @@ export default {
console.error('发料确认失败:', error)
this.$message.error('发料失败')
})
})
.catch(() => {
//
.finally(() => {
this.issueloading = false
})
},
async printViaServer(unitIds, printLabelType) {
if (!unitIds || unitIds.length === 0) {
console.warn('没有可打印的标签')
return
}
this.printLoading = true
try {
const printRequest = {
userId: localStorage.getItem('userName'),
username: localStorage.getItem('userName'),
site: localStorage.getItem('site'),
unitIds: unitIds,
labelType: printLabelType,
}
console.log('打印请求:', printRequest)
const { data } = await printLabelCommon(printRequest)
if (data.code === 200 || data.code === 0) {
this.$message.success(`打印任务已发送!`)
this.clearData()
} else {
this.$message.error(data.msg || '打印失败')
}
} catch (error) {
console.error('服务器打印失败:', error)
this.$message.error(`打印失败: ${error.message || error}`)
} finally {
this.printLoading = false
}
},
//
cancelIssue() {
@ -401,7 +426,7 @@ export default {
labelCode: label.labelCode,
batchNo: label.batchNo,
locationId: label.locationId || '',
quantity: label.quantity || label.availableQty
quantity: label.quantity || label.availableQty,
}
this.editIndex = index
this.showEditDialog = true
@ -414,7 +439,7 @@ export default {
labelCode: '',
batchNo: '',
locationId: '',
quantity: 0
quantity: 0,
}
this.editIndex = -1
},
@ -601,9 +626,13 @@ export default {
/* 内容区域 */
.content-area {
padding: 12px 16px;
}
.scrollable-content {
flex: 1;
overflow-y: auto;
padding: 12px 16px;
min-height: 0;
}
/* 材料卡片 */
@ -747,6 +776,8 @@ export default {
margin: 0 16px 12px;
border-radius: 0 0 8px 8px;
overflow: hidden;
display: flex;
flex-direction: column;
}
.list-header {
@ -757,6 +788,12 @@ export default {
font-size: 12px;
color: #666;
font-weight: 500;
flex-shrink: 0;
}
.list-body {
flex: 1;
min-height: 0;
}
.list-item {
@ -841,7 +878,6 @@ export default {
/* 工单列表 */
.work-order-list {
overflow-y: auto;
padding: 12px 16px;
}
.work-order-list {

522
src/views/modules/other-transaction/receiveFromTransit.vue

@ -14,97 +14,102 @@
<!-- 搜索框 -->
<div class="search-container">
<el-input clearable class="compact-input"
v-model="transactionId"
placeholder="请输入Transaction ID"
v-model="partNo"
placeholder="请输入物料编码"
prefix-icon="el-icon-search"
@keyup.enter.native="handleSearchTransaction"
ref="transactionInput"
ref="partNoInput"
/>
<el-button type="primary" class="big-button" @click="handleSearchTransaction" :loading="searching">
查询
</el-button>
</div>
<!-- 移库信息卡片 -->
<div class="material-info-card" v-if="transitInfo.transNo">
<div class="card-title">
<span class="title-label">移库单号</span>
<span class="title-value">{{ transitInfo.transNo }}</span>
<!-- 移库记录列表 -->
<div class="records-list" v-if="recordsList.length > 0">
<div class="section-title">
<div class="title-left">
<i class="el-icon-document"></i>
<span>移库记录 ({{ recordsList.length }})</span>
</div>
</div>
<div class="card-details">
<div class="detail-item">
<div class="detail-label">移出仓库</div>
<div class="detail-value">{{ transitInfo.fromWarehouse }}</div>
<!-- 每条移库记录 -->
<div
v-for="(record, index) in recordsList"
:key="index"
class="record-card"
:class="{ 'record-selected': selectedRecords.includes(index) }"
>
<!-- 记录头部 - 可点击展开/收起 -->
<div class="record-header" @click="toggleRecord(index)">
<div class="record-info">
<div class="record-part">{{ record.partNo }}</div>
<div class="record-desc">{{ record.partDescription }}</div>
</div>
<div class="detail-item">
<div class="detail-label">移入仓库</div>
<div class="detail-value">{{ transitInfo.toWarehouse }}</div>
<div class="record-qty">
<div class="qty-label">在途数量</div>
<div class="qty-value">{{ record.qtyInTransit }}</div>
</div>
<div class="detail-item">
<div class="detail-label">状态</div>
<div class="detail-value">
<span class="status-pending">{{ transitInfo.status }}</span>
</div>
<!-- 展开的编辑区域 -->
<div class="record-detail" v-if="expandedRecords.includes(index)">
<!-- 第一行接收数量 接收库位 -->
<div class="detail-row-group">
<div class="detail-col">
<div class="detail-label">接收数量 *</div>
<el-input
v-model="record.receiveQty"
size="small"
type="number"
placeholder="请输入数量"
@input="handleReceiveQtyChange(index)"
/>
</div>
<div class="detail-col">
<div class="detail-label">接收库位 *</div>
<el-input
v-model="record.receiveLocationNo"
size="small"
placeholder="请输入库位"
/>
</div>
</div>
<!-- 扫描区域 -->
<div class="section-title" v-if="transitInfo.transNo">
<div class="title-left">
<i class="el-icon-circle-check"></i>
<span>扫描箱/卷标签</span>
</div>
<!-- 第二行批次号 WDR号 -->
<div class="detail-row-group">
<div class="detail-col">
<div class="detail-label">批次号</div>
<el-input
v-model="record.receiveLotBatchNo"
size="small"
placeholder="请输入批次号"
/>
</div>
<!-- 扫描输入框 -->
<div class="scan-container" v-if="transitInfo.transNo">
<el-input clearable class="compact-input"
v-model="scanCode"
placeholder="请扫描箱/卷标签"
prefix-icon="el-icon-scan"
@keyup.enter.native="handleScan"
ref="scanInput"
<div class="detail-col">
<div class="detail-label">WDR号</div>
<el-input
v-model="record.receiveWaivDevRejNo"
size="small"
placeholder="请输入WDR号"
/>
</div>
<!-- 标签列表 -->
<div class="label-list" v-if="transitInfo.transNo">
<div class="list-header">
<div class="col-no">NO.</div>
<div class="col-label">标签条码</div>
<div class="col-part">物料编码</div>
<div class="col-unit">单位</div>
<div class="col-qty">标签数量</div>
<div class="col-status">状态</div>
</div>
<div
v-for="(label, index) in labelList"
:key="label.id"
class="list-item"
>
<div class="col-no">{{ labelList.length - index }}</div>
<div class="col-label">{{ label.labelCode }}</div>
<div class="col-part">{{ label.partNo }}</div>
<div class="col-unit">{{ label.unit || '个' }}</div>
<div class="col-qty">{{ label.quantity }}</div>
<div class="col-status">
<span class="status-success">已接收</span>
</div>
</div>
<!-- 空状态 -->
<div v-if="labelList.length === 0" class="empty-labels">
<p>暂无扫描标签</p>
<div v-if="recordsList.length === 0" class="empty-records">
<p>未查询到移库记录</p>
</div>
</div>
<!-- 底部操作按钮 -->
<div class="bottom-actions" v-if="transitInfo.transNo">
<button class="action-btn primary" @click="confirmReceive" :disabled="labelList.length === 0">
确认接收
<div class="bottom-actions" v-if="recordsList.length > 0">
<button
class="action-btn primary"
@click="confirmReceive"
:disabled="selectedRecords.length === 0"
>
确认收货
</button>
<button class="action-btn secondary" @click="cancelReceive">
取消
@ -120,24 +125,26 @@
</template>
<script>
import { getTransitRecord, scanTransitLabel, confirmTransitReceive } from '@/api/other-transaction/transit';
import { getTransitRecord, confirmTransitReceive } from '@/api/other-transaction/transit';
export default {
data() {
return {
transactionId: '',
scanCode: '',
transitInfo: {},
labelList: [],
partNo: '',
recordsList: [], //
expandedRecords: [], //
selectedRecords: [], //
searching: false,
errorMessage: ''
};
},
methods: {
//
/**
* 查询移库记录
*/
handleSearchTransaction() {
if (!this.transactionId.trim()) {
this.$message.warning('请输入Transaction ID');
if (!this.partNo.trim()) {
this.$message.warning('请输入物料编码');
return;
}
@ -145,125 +152,148 @@ export default {
this.errorMessage = '';
const params = {
transactionId: this.transactionId.trim(),
site: localStorage.getItem('site')
transactionId: this.partNo.trim(),
site: localStorage.getItem('site') || this.$store.state.user.site
};
getTransitRecord(params).then(({ data }) => {
this.searching = false;
if (data && data.code === 0) {
this.transitInfo = data.data;
this.$message.success('查询成功');
//
this.$nextTick(() => {
if (this.$refs.scanInput) {
this.$refs.scanInput.focus();
const result = data.data;
this.recordsList = result.detailList || [];
if (this.recordsList.length === 0) {
this.$message.warning('未查询到移库记录');
} else {
this.$message.success(`查询成功,共${this.recordsList.length}条记录`);
//
if (this.recordsList.length > 0) {
//
this.recordsList[0].receiveQty = this.recordsList[0].qtyInTransit;
//
this.expandedRecords = [0];
//
this.selectedRecords = [0];
}
}
});
} else {
this.$message.error(data.msg || '未找到移库记录');
this.transitInfo = {};
this.labelList = [];
this.$message.warning( '未找到移库记录');
this.recordsList = [];
}
}).catch(error => {
this.searching = false;
this.$message.error('查询失败');
this.transitInfo = {};
this.labelList = [];
this.recordsList = [];
});
},
//
handleScan() {
if (!this.scanCode.trim()) {
return;
/**
* 展开/收起记录
*/
toggleRecord(index) {
const idx = this.expandedRecords.indexOf(index);
if (idx > -1) {
this.expandedRecords.splice(idx, 1);
} else {
this.expandedRecords.push(index);
}
this.validateAndAddLabel(this.scanCode.trim());
this.scanCode = '';
},
//
validateAndAddLabel(labelCode) {
const params = {
labelCode: labelCode,
transNo: this.transitInfo.transNo,
site: localStorage.getItem('site'),
};
/**
* 接收数量变化 - 自动选中/取消选中
*/
handleReceiveQtyChange(index) {
const record = this.recordsList[index];
const receiveQty = parseFloat(record.receiveQty);
//
if (receiveQty && receiveQty > 0) {
if (!this.selectedRecords.includes(index)) {
this.selectedRecords.push(index);
}
} else {
// 0
const idx = this.selectedRecords.indexOf(index);
if (idx > -1) {
this.selectedRecords.splice(idx, 1);
}
}
},
scanTransitLabel(params).then(({ data }) => {
if (data && data.code === 0) {
//
const exists = this.labelList.find(item => item.labelCode === labelCode);
if (exists) {
this.$message.warning('该标签已扫描,请勿重复扫描');
/**
* 确认收货
*/
confirmReceive() {
if (this.selectedRecords.length === 0) {
this.$message.warning('请至少输入一条记录的接收数量');
return;
}
//
this.labelList.push({
id: Date.now(),
labelCode: labelCode,
partNo: data.labelInfo.partNo,
quantity: data.labelInfo.quantity,
unit: data.labelInfo.unit,
batchNo: data.labelInfo.batchNo,
locationId: data.labelInfo.locationId
});
//
for (let i = 0; i < this.selectedRecords.length; i++) {
const index = this.selectedRecords[i];
const record = this.recordsList[index];
this.$message.success('扫描成功');
} else {
this.$message.error(data.msg || '该标签与移库单不符,请检查');
const receiveQty = parseFloat(record.receiveQty);
if (!receiveQty || receiveQty <= 0) {
this.$message.warning(`${i + 1}条记录:请输入有效的接收数量`);
return;
}
}).catch(error => {
this.$message.error('扫描失败');
});
},
//
confirmReceive() {
if (this.labelList.length === 0) {
this.$message.warning('请先扫描标签');
if (receiveQty > parseFloat(record.qtyInTransit)) {
this.$message.warning(`${i + 1}条记录:接收数量不能大于在途数量`);
return;
}
this.$confirm('确认接收所有扫描的标签吗?', '提示', {
if (!record.receiveLocationNo || record.receiveLocationNo.trim() === '') {
this.$message.warning(`${i + 1}条记录:请输入接收库位`);
return;
}
}
//
const details = this.selectedRecords.map(index => {
return this.recordsList[index];
});
this.$confirm(`确认收货 ${this.selectedRecords.length} 条记录?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
const params = {
site: this.transitInfo.site,
transNo: this.transitInfo.transNo,
labels: this.labelList.map(label => ({
labelCode: label.labelCode,
quantity: label.quantity,
batchNo: label.batchNo,
partNo: label.partNo,
locationId: label.locationId || this.transitInfo.toLocation
}))
site: localStorage.getItem('site') || this.$store.state.user.site,
transNo: '', //
operatorId: localStorage.getItem('userId') || this.$store.state.user.userId,
operatorName: localStorage.getItem('userName') || this.$store.state.user.name,
details: details
};
confirmTransitReceive(params).then(({ data }) => {
if (data && data.code === 0) {
this.$message.success('收成功');
this.$router.back();
this.$message.success('收成功');
this.handleSearchTransaction();
} else {
this.$message.error(data.msg || '收失败');
this.$message.error(data.msg || '收失败');
}
}).catch(error => {
console.error('接收确认失败:', error);
this.$message.error('收失败');
console.error('收货失败:', error);
this.$message.error('收失败');
});
}).catch(() => {
//
});
},
//
/**
* 取消收货
*/
cancelReceive() {
if (this.labelList.length > 0) {
this.$confirm('取消后将清空已扫描的标签,确定取消吗?', '提示', {
if (this.selectedRecords.length > 0) {
this.$confirm('取消后将清空已选中的记录,确定取消吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '继续操作',
type: 'warning'
@ -279,10 +309,10 @@ export default {
},
mounted() {
// Transaction ID
//
this.$nextTick(() => {
if (this.$refs.transactionInput) {
this.$refs.transactionInput.focus();
if (this.$refs.partNoInput) {
this.$refs.partNoInput.focus();
}
});
}
@ -375,71 +405,6 @@ export default {
border-radius: 8px;
}
/* 物料信息卡片 */
.material-info-card {
background: white;
margin: 4px 16px;
padding: 6px 20px;
border-radius: 8px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
border: 1px solid #f0f0f0;
}
.card-title {
margin-bottom: 16px;
}
.title-label {
font-size: 11px;
color: #999;
display: block;
margin-bottom: 6px;
font-weight: normal;
}
.title-value {
font-size: 18px;
font-weight: bold;
color: #333;
line-height: 1.2;
margin-left: 20px;
}
.card-details {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 4px;
}
.detail-item {
flex: 1;
text-align: center;
min-width: 60px;
max-width: 60px;
}
.detail-label {
font-size: 11px;
color: #999;
margin-bottom: 6px;
font-weight: normal;
line-height: 1.2;
margin-left: -12px;
}
.detail-value {
font-size: 13px;
color: #333;
font-weight: 500;
line-height: 1.2;
margin-left: -12px;
}
.status-pending {
color: #E6A23C;
font-weight: 500;
}
/* 区域标题 */
.section-title {
@ -471,78 +436,129 @@ export default {
font-weight: 500;
}
/* 标签列表 */
.label-list {
/* 移库记录列表 */
.records-list {
flex: 1;
overflow-y: auto;
padding-bottom: 80px;
}
/* 记录卡片 */
.record-card {
background: white;
margin: 0 16px 12px;
border-radius: 0 0 8px 8px;
overflow: hidden;
margin: 8px 16px;
border-radius: 8px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
border: 2px solid transparent;
transition: all 0.2s;
}
.list-header {
.record-card.record-selected {
border-color: #17B3A3;
box-shadow: 0 2px 8px rgba(23, 179, 163, 0.3);
}
.record-header {
display: flex;
background: #f8f9fa;
padding: 12px 8px;
border-bottom: 1px solid #e0e0e0;
justify-content: space-between;
padding: 12px;
cursor: pointer;
}
.record-info {
flex: 1;
}
.record-part {
font-size: 15px;
font-weight: bold;
color: #333;
margin-bottom: 6px;
}
.record-desc {
font-size: 12px;
color: #666;
font-weight: 500;
margin-bottom: 8px;
line-height: 1.4;
}
.list-item {
display: flex;
padding: 12px 8px;
border-bottom: 1px solid #f0f0f0;
font-size: 12px;
.record-location {
font-size: 11px;
color: #999;
}
.label-text {
color: #999;
margin-right: 4px;
}
.value-text {
color: #333;
font-weight: 500;
margin-right: 12px;
}
.list-item:last-child {
border-bottom: none;
.record-qty {
text-align: right;
min-width: 80px;
}
.col-no {
width: 20px;
text-align: center;
.qty-label {
font-size: 11px;
color: #999;
margin-bottom: 4px;
}
.col-label {
flex: 2;
text-align: center;
.qty-value {
font-size: 20px;
font-weight: bold;
color: #E6A23C;
line-height: 1.2;
}
.col-part {
flex: 2;
text-align: center;
.qty-unit {
font-size: 11px;
color: #999;
margin-top: 2px;
}
.col-unit {
width: 40px;
text-align: center;
/* 记录详情 */
.record-detail {
border-top: 1px solid #f0f0f0;
padding: 12px;
background: #fafafa;
}
.col-qty {
width: 60px;
text-align: center;
/* 两列布局 */
.detail-row-group {
display: flex;
gap: 12px;
margin-bottom: 12px;
}
.col-status {
width: 60px;
text-align: center;
.detail-row-group:last-child {
margin-bottom: 0;
}
.detail-col {
flex: 1;
}
.status-success {
color: #67C23A;
.detail-label {
font-size: 12px;
color: #666;
margin-bottom: 6px;
font-weight: 500;
}
.empty-labels {
.empty-records {
padding: 40px 20px;
text-align: center;
color: #999;
}
.empty-labels p {
.empty-records p {
margin: 0;
font-size: 14px;
}

Loading…
Cancel
Save