Browse Source

销售退货报废

master
han\hanst 3 months ago
parent
commit
b274f43e67
  1. 3
      src/api/sales-return/sales-return.js
  2. 2
      src/views/modules/sales-return/index.vue
  3. 375
      src/views/modules/sales-return/receive.vue
  4. 18
      src/views/modules/sales-return/sales-return-inbound.vue
  5. 833
      src/views/modules/sales-return/sales-return-scrap.vue

3
src/api/sales-return/sales-return.js

@ -13,6 +13,9 @@ export const processReturn = data => createAPI(`salesreturn/processReturn`, 'pos
// 销售退货入库(调用IFS API) // 销售退货入库(调用IFS API)
export const processRmaInbound = data => createAPI(`salesreturn/processRmaInbound`, 'post', data) export const processRmaInbound = data => createAPI(`salesreturn/processRmaInbound`, 'post', data)
// 销售退货报废(调用IFS RMAScrapPart API)
export const processRmaScrap = data => createAPI(`salesreturn/processRmaScrap`, 'post', data)
// 打印标签(通用打印接口) // 打印标签(通用打印接口)
export const printLabelCommon = data => createAPI('/label/setting/printLabelCommon', 'post', data) export const printLabelCommon = data => createAPI('/label/setting/printLabelCommon', 'post', data)

2
src/views/modules/sales-return/index.vue

@ -30,7 +30,7 @@ export default {
return { return {
buttons: [ buttons: [
{ icon: 'shopping-cart-o', label: '退货入库', iconClass: 'purchase', to: 'salereturn-inbound' }, { icon: 'shopping-cart-o', label: '退货入库', iconClass: 'purchase', to: 'salereturn-inbound' },
{ icon: 'orders-o', label: '报废', iconClass: 'scrap', to: 'salereturn-scrap' }
{ icon: 'delete-o', label: '报废', iconClass: 'purchase', to: 'salereturn-scrap' }
] ]
} }
} }

375
src/views/modules/sales-return/receive.vue

@ -1,375 +0,0 @@
<template>
<div class="receive-container">
<van-nav-bar title="退货收货" left-arrow @click-left="$router.back()" />
<!-- 退货单信息 -->
<div class="return-info">
<div class="info-header">
<div class="return-no">{{ returnInfo.returnNo }}</div>
<div class="return-status">{{ getStatusText(returnInfo.status) }}</div>
</div>
<van-cell-group>
<van-cell title="原订单号" :value="returnInfo.originalOrderNo" />
<van-cell title="客户" :value="returnInfo.customerName" />
<van-cell title="退货原因" :value="returnInfo.returnReason" />
<van-cell title="申请时间" :value="returnInfo.applyTime" />
</van-cell-group>
</div>
<!-- 退货明细 -->
<div class="goods-section">
<div class="section-title">退货明细</div>
<div
v-for="(item, index) in goodsList"
:key="index"
class="goods-item"
>
<div class="goods-info">
<div class="goods-name">{{ item.productCode }} - {{ item.productName }}</div>
<div class="goods-spec">规格{{ item.specification }}</div>
<div class="goods-quantity">
申请退货{{ item.returnQuantity }} | 实际收货{{ item.receivedQuantity }}
</div>
</div>
<div class="receive-input">
<van-stepper
v-model="item.currentReceive"
:min="0"
:max="item.returnQuantity - item.receivedQuantity"
integer
/>
</div>
</div>
</div>
<!-- 质量检验 -->
<div class="quality-section">
<div class="section-title">质量检验</div>
<van-field
v-model="qualityInfo.inspector"
label="检验员"
placeholder="请输入检验员"
/>
<van-field
v-model="qualityInfo.qualityResult"
label="检验结果"
placeholder="请选择检验结果"
readonly
is-link
@click="showQualityPicker = true"
/>
<van-field
v-model="qualityInfo.qualityRemark"
label="检验备注"
type="textarea"
placeholder="请输入检验备注"
rows="3"
/>
</div>
<!-- 处理方式 -->
<div class="handle-section">
<div class="section-title">处理方式</div>
<van-field
v-model="handleInfo.handleMethod"
label="处理方式"
placeholder="请选择处理方式"
readonly
is-link
@click="showHandlePicker = true"
/>
<van-field
v-model="handleInfo.targetLocation"
label="目标库位"
placeholder="请选择或扫描库位"
readonly
is-link
@click="showLocationPicker = true"
>
<template #right-icon>
<van-icon name="scan" @click.stop="handleScanLocation" />
</template>
</van-field>
</div>
<!-- 备注 -->
<div class="remark-section">
<van-field
v-model="remark"
label="备注"
type="textarea"
placeholder="请输入处理备注"
rows="3"
autosize
/>
</div>
<!-- 底部按钮 -->
<div class="bottom-actions">
<van-button
type="primary"
block
:loading="submitting"
@click="handleSubmit"
>
确认收货
</van-button>
</div>
<!-- 选择器 -->
<van-popup v-model="showQualityPicker" position="bottom">
<van-picker
:columns="qualityColumns"
@confirm="onQualityConfirm"
@cancel="showQualityPicker = false"
/>
</van-popup>
<van-popup v-model="showHandlePicker" position="bottom">
<van-picker
:columns="handleColumns"
@confirm="onHandleConfirm"
@cancel="showHandlePicker = false"
/>
</van-popup>
<van-popup v-model="showLocationPicker" position="bottom">
<van-picker
:columns="locationColumns"
@confirm="onLocationConfirm"
@cancel="showLocationPicker = false"
/>
</van-popup>
</div>
</template>
<script>
export default {
name: 'SalesReturnReceive',
data() {
return {
returnInfo: {
returnNo: 'RT202401001',
originalOrderNo: 'SO202401001',
customerName: '客户A有限公司',
returnReason: '质量问题',
applyTime: '2024-01-15 10:30',
status: 0
},
goodsList: [
{
productCode: 'PROD001',
productName: '成品A',
specification: '标准规格',
returnQuantity: 10,
receivedQuantity: 0,
currentReceive: 0
}
],
qualityInfo: {
inspector: '',
qualityResult: '',
qualityRemark: ''
},
handleInfo: {
handleMethod: '',
targetLocation: ''
},
remark: '',
submitting: false,
showQualityPicker: false,
showHandlePicker: false,
showLocationPicker: false,
qualityColumns: [
'合格-重新入库',
'不合格-报废',
'不合格-返修',
'待定-暂存'
],
handleColumns: [
'重新入库',
'报废处理',
'返修处理',
'暂存处理'
],
locationColumns: [
'RT01-01-01',
'RT01-01-02',
'FG01-01-01',
'FG01-01-02',
'SCRAP-01'
]
}
},
mounted() {
this.loadReturnData()
},
methods: {
loadReturnData() {
const returnNo = this.$route.params.orderNo
console.log('加载退货单数据:', returnNo)
},
handleScanLocation() {
this.$toast('扫描库位功能开发中...')
},
onQualityConfirm(value) {
this.qualityInfo.qualityResult = value
this.showQualityPicker = false
},
onHandleConfirm(value) {
this.handleInfo.handleMethod = value
this.showHandlePicker = false
},
onLocationConfirm(value) {
this.handleInfo.targetLocation = value
this.showLocationPicker = false
},
async handleSubmit() {
//
const hasReceive = this.goodsList.some(item => item.currentReceive > 0)
if (!hasReceive) {
this.$toast('请输入收货数量')
return
}
//
if (!this.qualityInfo.qualityResult) {
this.$toast('请选择检验结果')
return
}
//
if (!this.handleInfo.handleMethod) {
this.$toast('请选择处理方式')
return
}
//
if (!this.handleInfo.targetLocation) {
this.$toast('请选择目标库位')
return
}
this.submitting = true
try {
await new Promise(resolve => setTimeout(resolve, 2000))
this.$toast.success('收货成功')
this.$router.back()
} catch (error) {
this.$toast.fail('收货失败')
} finally {
this.submitting = false
}
},
getStatusText(status) {
const statusMap = {
0: '待收货',
1: '质检中',
2: '已完成'
}
return statusMap[status] || '未知'
}
}
}
</script>
<style scoped>
.receive-container {
min-height: 100vh;
background-color: #f7f8fa;
padding-bottom: 80px;
}
.return-info {
background: white;
margin-bottom: 10px;
}
.info-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px;
border-bottom: 1px solid #ebedf0;
}
.return-no {
font-size: 18px;
font-weight: bold;
color: #323233;
}
.return-status {
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
color: white;
background-color: #ff976a;
}
.goods-section,
.quality-section,
.handle-section,
.remark-section {
background: white;
margin-bottom: 10px;
}
.section-title {
padding: 16px;
font-size: 16px;
font-weight: bold;
color: #323233;
border-bottom: 1px solid #ebedf0;
}
.goods-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px;
border-bottom: 1px solid #ebedf0;
}
.goods-item:last-child {
border-bottom: none;
}
.goods-info {
flex: 1;
}
.goods-name {
font-size: 16px;
font-weight: bold;
color: #323233;
margin-bottom: 4px;
}
.goods-spec {
font-size: 12px;
color: #969799;
margin-bottom: 4px;
}
.goods-quantity {
font-size: 14px;
color: #646566;
}
.receive-input {
margin-left: 16px;
}
.bottom-actions {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 16px;
background: white;
border-top: 1px solid #ebedf0;
}
</style>

18
src/views/modules/sales-return/sales-return-inbound.vue

@ -393,6 +393,7 @@ export default {
partDesc: row.salesPartDesc, partDesc: row.salesPartDesc,
qtyToReturn: row.qtyToReturn, qtyToReturn: row.qtyToReturn,
qtyReceived: row.qtyReceived || 0, qtyReceived: row.qtyReceived || 0,
qtyScrapped: row.qtyScrapped || 0,
transQty: '', transQty: '',
uom: row.returnUOM, uom: row.returnUOM,
locationNo: 'AS', locationNo: 'AS',
@ -514,23 +515,12 @@ export default {
const transQty = parseFloat(item.transQty) || 0; const transQty = parseFloat(item.transQty) || 0;
const qtyToReturn = parseFloat(item.qtyToReturn) || 0; const qtyToReturn = parseFloat(item.qtyToReturn) || 0;
const qtyReceived = parseFloat(item.qtyReceived) || 0; const qtyReceived = parseFloat(item.qtyReceived) || 0;
const qtyScrapped = parseFloat(item.qtyScrapped) || 0;
// //
if (qtyReceived + transQty > qtyToReturn) { if (qtyReceived + transQty > qtyToReturn) {
return this.$message.error( `已接收数量(${qtyReceived}) + 本次入库数量(${transQty}) = ${qtyReceived + transQty},超过退货数量(${qtyToReturn})`);
/*try {
await this.$confirm(
`已接收数量(${qtyReceived}) + 本次入库数量(${transQty}) = ${qtyReceived + transQty},超过退货数量(${qtyToReturn}),是否继续?`,
'数量超出提示',
{
confirmButtonText: '继续',
cancelButtonText: '取消',
type: 'warning'
}
);
} catch (error) {
return;
}*/
return this.$message.error( `已接收数量(${qtyReceived}) + 已报废数量(${qtyScrapped}) +
本次入库数量(${transQty}) = ${qtyReceived + transQty}超过退货数量(${qtyToReturn})`);
} }
this.loadingText = '提交中...'; this.loadingText = '提交中...';

833
src/views/modules/sales-return/sales-return-scrap.vue

@ -1,6 +1,9 @@
<template> <template>
<div> <div>
<div class="pda-container">
<div class="pda-container" v-loading.fullscreen.lock="fullscreenLoading"
element-loading-background="rgba(255, 255, 255, 0.3)"
element-loading-spinner="el-icon-loading"
:element-loading-text="loadingText">
<!-- 头部栏 --> <!-- 头部栏 -->
<div class="header-bar"> <div class="header-bar">
<div class="header-left" @click="handleBack"> <div class="header-left" @click="handleBack">
@ -12,127 +15,163 @@
</div> </div>
</div> </div>
<!-- 搜索框 -->
<div class="search-container">
<div style="overflow-y: auto">
<!-- Step 1: 扫描RMA号 -->
<div v-if="processFlag === 1">
<div class="scan-box" style="margin: 2px;">
<el-input <el-input
v-model="scanRma"
placeholder="扫描RMA条码或输入RMA号"
prefix-icon="el-icon-search"
clearable
v-model="scanRmaNo"
placeholder="扫描或输入RMA号"
@keyup.enter.native="searchRmaList" @keyup.enter.native="searchRmaList"
ref="scanRmaRef" ref="scanRmaRef"
clearable
/> />
</div> </div>
<div class="main-content">
<!-- RMA明细选择 -->
<div v-if="rmaList.length > 0" class="rma-list">
<div class="list-title">选择RMA明细行号</div>
<el-form>
<el-row v-for="(rmaDetail, index) in rmaList[0].detailList" :key="index" class="rma-row">
<!-- RMA明细列表 -->
<div class="item-list" v-if="rmaList.length > 0" style="margin: 2px;">
<el-form label-position="top" style="margin: 3px;">
<el-row :gutter="5"
v-for="(rmaLine, index) in rmaList" :key="index"
:class="index < rmaList.length - 1 ? 'bottom-line-row' : ''">
<el-col :span="8">
<el-form-item label="物料编码">
<span>{{ rmaLine.invPartNo }}</span>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="行号">
<span>{{ rmaLine.rmaLineNo }}</span>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="">
<el-button type="text" class="recvButton"
@click="selectRmaLine(rmaLine)"
style="margin-top: 10px;margin-left: 20px"
size="small">报废</el-button>
</el-form-item>
</el-col>
<el-col :span="24"> <el-col :span="24">
<div class="rma-item" @click="selectRmaDetail(rmaDetail)" :class="{ 'selected': selectedDetail && selectedDetail.partNo === rmaDetail.partNo }">
<div class="item-info">
<span class="part-no">{{ rmaDetail.partNo }}</span>
<span class="batch-qty">批号:{{ rmaDetail.batchNo }} | 数量:{{ rmaDetail.processQty }}</span>
</div>
<div class="item-status">
<i class="el-icon-check" v-if="isInReturnList(rmaDetail)"></i>
</div>
</div>
<el-form-item label="物料描述">
<span>{{ rmaLine.salesPartDesc }}</span>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="退货数量">
<span>{{ rmaLine.qtyToReturn }}</span>
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item label="已接收">
<span>{{ rmaLine.qtyReceived || 0 }}</span>
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item label="单位">
<span>{{ rmaLine.returnUOM }}</span>
</el-form-item>
</el-col>
<el-col :span="10">
<el-form-item label="状态">
<el-tag :type="rmaLine.status === 'Released'||rmaLine.status==='PartiallyReceived'
? 'success' : 'warning'" size="mini">
{{ rmaLine.status }}
</el-tag>
</el-form-item>
</el-col> </el-col>
</el-row> </el-row>
</el-form> </el-form>
</div> </div>
<!-- 退货明细列表 -->
<div v-if="returnList.length > 0">
<div class="return-list-title">退货明细列表</div>
<div v-for="(detail, dIdx) in returnList" :key="dIdx" class="detail-block">
<el-card shadow="hover" class="detail-card">
<div slot="header" class="detail-header">
<span>明细{{ dIdx + 1 }}</span>
<el-button type="danger" icon="el-icon-delete" circle size="mini" @click="removeDetail(dIdx)" v-if="returnList.length > 1" style="float:right;"></el-button>
</div>
<div class="detail-info">
<div class="info-row">
<span class="info-label">物料:</span>
<span class="info-value">{{ detail.partNo }}</span>
</div>
<div class="info-row">
<span class="info-label">数量:</span>
<span class="info-value">{{ detail.processQty }}</span>
</div>
<div class="info-row">
<span class="info-label">批号:</span>
<span class="info-value">{{ detail.batchNo }}</span>
</div>
</div> </div>
<!-- 包装单元和报废原因 -->
<div class="pack-unit-area">
<div class="pack-actions">
<!-- 创建包装单元 -->
<div class="action-group">
<div class="action-title">创建包装单元</div>
<el-row :gutter="6">
<el-col :span="10">
<el-input v-model="detail.newPackCode" placeholder="包装编码" size="small" />
<!-- Step 2: 报废明细 -->
<div v-if="processFlag === 2">
<el-form label-position="top" class="form-section" style="margin: 5px;">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="RMA号">
<el-input v-model="scrapItem.rmaNo" disabled />
</el-form-item>
</el-col> </el-col>
<el-col :span="8">
<el-input v-model="detail.newPackQty" placeholder="数量" type="number" size="small" />
<el-col :span="12">
<el-form-item label="行号">
<el-input v-model="scrapItem.rmaLineNo" disabled />
</el-form-item>
</el-col> </el-col>
<el-col :span="6">
<el-button type="primary" size="small" @click="createPackUnit(detail)" :disabled="!detail.newPackCode || !detail.newPackQty">创建</el-button>
<el-col :span="12">
<el-form-item label="物料编码">
<el-input v-model="scrapItem.partNo" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="计量单位">
<el-input v-model="scrapItem.uom" disabled />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="物料描述">
<el-input v-model="scrapItem.partDesc" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="退货数量">
<el-input v-model="scrapItem.qtyToReturn" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="已接收">
<el-input v-model="scrapItem.qtyReceived" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="此次报废数量" required>
<el-input v-model="scrapItem.scrapQty" type="number"
placeholder="请输入报废数量" ref="qtyRmaRef" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="批次号" required>
<el-input v-model="scrapItem.batchNo"
placeholder="请输入批次号(如:2886435-*-*-1)" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="报废原因" required>
<el-select v-model="scrapItem.rejectReason"
placeholder="请选择报废原因"
style="width: 100%">
<el-option
v-for="reason in rejectReasons"
:key="reason.value"
:label="reason.label"
:value="reason.value">
</el-option>
</el-select>
</el-form-item>
</el-col> </el-col>
</el-row> </el-row>
</div>
<!-- 扫描包装单元 -->
<div class="action-group" v-if="detail.packUnitList.length > 0">
<div class="action-title">扫描包装单元</div>
<el-row :gutter="6">
<el-col :span="18">
<el-input
v-model="detail.scanPackCode"
placeholder="扫描包装单元条码"
prefix-icon="el-icon-scan"
size="small"
@keyup.enter.native="scanPackUnit(detail)"
/>
<el-row :gutter="20">
<el-col :span="8" style="margin-top: 10px">
<el-form-item>
<el-button type="text" style="font-size: 16px;margin-left: 30px"
@click="processFlag = 1">回退</el-button>
</el-form-item>
</el-col> </el-col>
<el-col :span="6">
<el-button type="primary" size="small" @click="scanPackUnit(detail)" :disabled="!detail.scanPackCode">扫描</el-button>
<el-col :span="8" style="margin-top: 10px">
<el-form-item>
<el-button type="text" style="font-size: 16px;margin-left: 20px"
@click="confirmScrap">报废</el-button>
</el-form-item>
</el-col>
<el-col :span="8" style="margin-top: 10px">
<el-form-item>
<el-button type="text" style="font-size: 16px;margin-left: 10px"
@click="$router.push('/')">退出</el-button>
</el-form-item>
</el-col> </el-col>
</el-row> </el-row>
</div>
<!-- 报废原因选择 -->
<div class="action-group">
<div class="action-title">报废原因</div>
<el-select v-model="detail.scrapReason" placeholder="请选择报废原因" size="small" style="width: 100%">
<el-option label="物料损坏" value="DAMAGED"></el-option>
<el-option label="物料过期" value="EXPIRED"></el-option>
<el-option label="质量问题" value="QUALITY_ISSUE"></el-option>
<el-option label="物料过时" value="OBSOLETE"></el-option>
<el-option label="其他原因" value="OTHER"></el-option>
</el-select>
</div>
</div>
<!-- 包装单元列表 -->
<div v-if="detail.packUnitList.length > 0" class="pack-list">
<div v-for="(pack, pIdx) in detail.packUnitList" :key="pIdx" class="pack-item">
{{ pack.code }} ({{ pack.qty }})
<span :class="pack.scanned ? 'status-ok' : 'status-wait'">
{{ pack.scanned ? '✓' : '○' }}
</span>
<el-button type="text" @click="removePackUnit(detail, pIdx)" class="delete-btn">删除</el-button>
</div>
</div>
</div>
</el-card>
</div>
<el-button type="success" class="submit-btn" @click="submitReturn">确认报废</el-button>
</el-form>
</div> </div>
</div> </div>
</div> </div>
@ -140,180 +179,226 @@
</template> </template>
<script> <script>
import { getRmaList, processReturn } from "@/api/sales-return/sales-return.js";
import { getRmaList, processRmaScrap } from "@/api/sales-return/sales-return.js";
export default { export default {
name: 'SalesReturnScrap',
data() { data() {
return { return {
scanRma: "",
processFlag: 1, // 1=RMA 2=
scanRmaNo: '',
rmaList: [], rmaList: [],
returnList: [], // 退
selectedDetail: null, // RMA
processType: "scrap", //
scrapItem: {
rmaNo: '',
rmaLineNo: '',
partNo: '',
partDesc: '',
qtyToReturn: '',
qtyReceived: 0,
scrapQty: '',
uom: '',
batchNo: '',
rejectReason: 'DP05',
site: localStorage.getItem('site') site: localStorage.getItem('site')
},
site: localStorage.getItem('site'),
fullscreenLoading: false,
loadingText: '加载中...',
// IFS
rejectReasons: [
{ value: 'DP05', label: 'DP05 - 其他' },
{ value: 'DP01', label: 'DP01 - 客户原因' },
{ value: 'DP02', label: 'DP02 - 质量问题' },
{ value: 'DP03', label: 'DP03 - 包装破损' },
{ value: 'DP04', label: 'DP04 - 运输损坏' }
]
}; };
}, },
methods: { methods: {
handleBack() { handleBack() {
if (this.processFlag === 1) {
this.$router.back(); this.$router.back();
},
searchRmaList() {
if (!this.scanRma) return (this.rmaList = []);
getRmaList({ rmaNo: this.scanRma,site:this.site }).then(({ data }) => {
if (data.code === 0) this.rmaList = data.rows;
});
},
// RMA
selectRmaDetail(rmaDetail) {
this.selectedDetail = rmaDetail;
// 退
if (!this.isInReturnList(rmaDetail)) {
this.addToReturnList(rmaDetail);
}
this.$message.success(`已选择物料: ${rmaDetail.partNo}`);
},
addToReturnList(rmaDetail) {
if (this.isInReturnList(rmaDetail)) return;
this.returnList.push({
site:this.site,
partNo: rmaDetail.partNo,
processQty: rmaDetail.processQty,
batchNo: rmaDetail.batchNo,
scrapReason: "",
packUnitList: [],
newPackCode: "",
newPackQty: "",
scanPackCode: ""
});
},
isInReturnList(rmaDetail) {
return this.returnList.some(
d => d.partNo === rmaDetail.partNo && d.batchNo === rmaDetail.batchNo
);
},
removeDetail(idx) {
this.returnList.splice(idx, 1);
} else {
this.processFlag = 1;
}
}, },
//
createPackUnit(detail) {
if (!detail.newPackCode || !detail.newPackQty) {
this.$message.warning('请输入包装编码和数量');
/**
* 搜索RMA列表
*/
searchRmaList() {
if (!this.scanRmaNo) {
this.rmaList = [];
return; return;
} }
//
const exists = detail.packUnitList.find(pack => pack.code === detail.newPackCode);
if (exists) {
this.$message.error('包装编码已存在');
return;
}
this.loadingText = '搜索中...';
this.fullscreenLoading = true;
detail.packUnitList.push({
code: detail.newPackCode,
qty: parseFloat(detail.newPackQty),
scanned: false,
createTime: new Date()
getRmaList({
rmaNo: this.scanRmaNo.trim(),
site: this.site
}).then(({ data }) => {
if (data && data.code === 0) {
this.rmaList = data.rows || [];
if (this.rmaList.length === 0) {
this.$message.warning('未找到匹配的RMA记录');
}
} else {
this.$message.error(data.msg || '查询失败');
this.rmaList = [];
}
this.$nextTick(() => {
if (this.$refs.scanRmaRef) {
this.$refs.scanRmaRef.blur();
}
});
}).catch(error => {
console.error('搜索失败:', error);
this.$message.error('搜索失败,请重试');
this.rmaList = [];
}).finally(() => {
this.fullscreenLoading = false;
}); });
},
//
detail.newPackCode = "";
detail.newPackQty = "";
/**
* 选择RMA行
*/
selectRmaLine(row) {
//
if (row.status !== 'Released' && row.status !== 'PartiallyReceived') {
return this.$message.warning(`RMA状态为 ${row.status},只有Released或PartiallyReceived状态才能报废`);
}
this.scrapItem = {
rmaNo: this.scanRmaNo.trim(),
rmaLineNo: row.rmaLineNo,
partNo: row.invPartNo,
partDesc: row.salesPartDesc,
qtyToReturn: row.qtyToReturn,
qtyReceived: row.qtyReceived || 0,
qtyScrapped: row.qtyScrapped || 0,
scrapQty: '',
uom: row.returnUOM,
batchNo: `${this.scanRmaNo}-${row.rmaLineNo}-1`,
rejectReason: 'DP05',
site: this.site
};
this.processFlag = 2;
this.$nextTick(() => {
this.$refs.qtyRmaRef && this.$refs.qtyRmaRef.focus()
});
this.$message.success('包装单元创建成功');
}, },
//
scanPackUnit(detail) {
if (!detail.scanPackCode) {
this.$message.warning('请输入要扫描的包装单元条码');
return;
}
/**
* 确认报废
*/
confirmScrap() {
if (this.fullscreenLoading) return;
const pack = detail.packUnitList.find(p => p.code === detail.scanPackCode);
if (!pack) {
this.$message.error('未找到对应的包装单元');
return;
}
const item = this.scrapItem;
if (pack.scanned) {
this.$message.warning('该包装单元已经扫描过了');
return;
//
if (!item.scrapQty || parseFloat(item.scrapQty) <= 0) {
return this.$message.error("请输入有效的报废数量");
} }
pack.scanned = true;
pack.scanTime = new Date();
detail.scanPackCode = "";
this.$message.success('扫描成功');
},
//
removePackUnit(detail, idx) {
detail.packUnitList.splice(idx, 1);
this.$message.success('包装单元已移除');
},
submitReturn() {
if (!this.scanRma || this.returnList.length === 0) {
this.$message.error("请先扫描RMA号并选择退货明细");
return;
if (!item.batchNo || item.batchNo.trim() === '') {
return this.$message.error("请输入批次号");
} }
//
const missingReason = this.returnList.some(detail => !detail.scrapReason);
if (missingReason) {
this.$message.error("请为每个明细选择报废原因");
return;
if (!item.rejectReason || item.rejectReason.trim() === '') {
return this.$message.error("请选择报废原因");
} }
//
const hasUnscannedPacks = this.returnList.some(detail =>
detail.packUnitList.length > 0 && detail.packUnitList.some(pack => !pack.scanned)
);
const scrapQty = parseFloat(item.scrapQty);
const qtyToReturn = parseFloat(item.qtyToReturn) || 0;
const qtyReceived = parseFloat(item.qtyReceived) || 0;
const qtyScrapped = parseFloat(item.qtyScrapped) || 0;
if (hasUnscannedPacks) {
this.$message.error("请先扫描所有包装单元");
return;
//
if (scrapQty > qtyToReturn-qtyReceived-qtyScrapped) {
return this.$message.error(`报废数量(${scrapQty})不能超过退货数量(${qtyToReturn})-已接收数量(${qtyReceived})-已报废数量(${qtyScrapped})=${qtyToReturn - qtyReceived - qtyScrapped}`);
} }
const submitData = {
rmaNo: this.scanRma,
processType: this.processType,
detailList: this.returnList.map(detail => ({
...detail,
packUnitList: detail.packUnitList.filter(pack => pack.scanned)
}))
};
this.$confirm('确认执行报废操作吗?', '提示', {
confirmButtonText: '确认',
//
this.$confirm(`确认报废 ${scrapQty} ${item.uom}${item.partNo}`, '报废确认', {
confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'
}).then(() => { }).then(() => {
processReturn(submitData).then(({ data }) => {
if (data.code === 0) {
this.$message.success('报废成功');
this.submitScrap();
}).catch(() => {
//
});
},
/**
* 提交报废
*/
submitScrap() {
this.loadingText = '提交报废中...';
this.fullscreenLoading = true;
//
const submitData = {
site: this.scrapItem.site,
rmaNo: this.scrapItem.rmaNo,
rmaLineNo: parseInt(this.scrapItem.rmaLineNo),
partNo: this.scrapItem.partNo,
scrapQty: parseFloat(this.scrapItem.scrapQty),
batchNo: this.scrapItem.batchNo.trim(),
configurationId: '*',
conditionCode: null,
serialNo: '*',
engChgLevel: '1',
wdr: '*',
rejectReason: this.scrapItem.rejectReason
};
console.log('报废提交数据:', submitData);
processRmaScrap(submitData).then(({ data }) => {
if (data && data.code === 0) {
this.$message.success("报废成功");
this.resetForm(); this.resetForm();
} else { } else {
this.$message.error(data.msg || '报废失败'); this.$message.error(data.msg || '报废失败');
} }
}).catch(error => { }).catch(error => {
console.error('提交失败:', error);
this.$message.error('报废失败');
});
}).catch(() => {
//
console.error('报废失败:', error);
this.$message.error('网络错误,请重试');
}).finally(() => {
this.fullscreenLoading = false;
}); });
}, },
//
/**
* 重置表单
*/
resetForm() { resetForm() {
this.scanRma = "";
this.processFlag = 1;
this.scanRmaNo = '';
this.rmaList = []; this.rmaList = [];
this.returnList = [];
this.selectedDetail = null;
this.processType = "scrap";
//
this.scrapItem = {
rmaNo: '',
rmaLineNo: '',
partNo: '',
partDesc: '',
qtyToReturn: '',
qtyReceived: 0,
scrapQty: '',
uom: '',
batchNo: '',
rejectReason: '',
site: this.site
};
this.$nextTick(() => { this.$nextTick(() => {
if (this.$refs.scanRmaRef) { if (this.$refs.scanRmaRef) {
this.$refs.scanRmaRef.focus(); this.$refs.scanRmaRef.focus();
@ -321,17 +406,23 @@ export default {
}); });
} }
}, },
mounted() { mounted() {
this.$nextTick(() => this.$refs.scanRmaRef && this.$refs.scanRmaRef.focus());
this.$nextTick(() =>
this.$refs.scanRmaRef && this.$refs.scanRmaRef.focus()
);
this.rejectReason = 'DP05'
} }
}; };
</script> </script>
<style scoped> <style scoped>
.pda-container { .pda-container {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
background: #f5f5f5; background: #f5f5f5;
min-height: 100vh;
padding-bottom: 20px;
} }
/* 头部栏 */ /* 头部栏 */
@ -339,10 +430,11 @@ export default {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
background: #17b3a3;
color: #fff;
padding: 12px 16px;
font-size: 16px;
padding: 8px 16px;
background: #17B3A3;
color: white;
height: 40px;
min-height: 40px;
} }
.header-left { .header-left {
@ -356,263 +448,80 @@ export default {
font-size: 18px; font-size: 18px;
} }
.header-right {
cursor: pointer;
}
/* 搜索容器 */
.search-container {
padding: 12px 16px;
background: white;
}
.search-container .el-input {
font-size: 16px;
}
/* RMA明细选择 */
.rma-list {
padding: 0 16px;
}
.list-title {
.header-left span {
font-size: 16px; font-size: 16px;
font-weight: bold;
color: #17b3a3;
margin: 12px 0 8px 0;
}
.rma-row {
margin-bottom: 8px;
}
.rma-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px;
background: white;
border-radius: 6px;
cursor: pointer;
border: 1px solid #e0e0e0;
min-height: 60px;
}
.rma-item.selected {
border-color: #17b3a3;
background: #f0f9ff;
}
.rma-item:active {
background: #e6f7ff;
}
.item-info {
flex: 1;
display: flex;
flex-direction: column;
align-items: flex-start;
}
.part-no {
font-size: 16px;
font-weight: bold;
color: #333;
margin-bottom: 4px;
}
.batch-qty {
font-size: 14px;
color: #666;
}
.item-status {
font-size: 20px;
color: #17b3a3;
}
/* 退货明细列表 */
.return-list-title {
font-size: 16px;
font-weight: bold;
color: #17b3a3;
margin: 12px 16px 8px 16px;
}
.detail-card {
margin: 0 16px 12px 16px;
}
.detail-header {
font-weight: bold;
font-size: 15px;
color: #333;
border-left: 4px solid #17b3a3;
padding-left: 8px;
}
/* 明细信息显示 */
.detail-info {
margin-bottom: 12px;
}
.info-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 6px 0;
border-bottom: 1px solid #f0f0f0;
}
.info-row:last-child {
border-bottom: none;
}
.info-label {
font-size: 14px;
color: #666;
font-weight: 500; font-weight: 500;
} }
.info-value {
.header-right {
cursor: pointer;
font-size: 14px; font-size: 14px;
color: #333;
font-weight: 500;
}
/* 包装单元区域 */
.pack-unit-area {
margin-top: 0;
padding: 12px;
background: #f8f9fa;
border-radius: 6px;
}
.pack-actions {
margin-bottom: 8px;
}
.action-group {
margin-bottom: 10px;
}
.action-group:last-child {
margin-bottom: 0;
}
.action-title {
font-size: 13px;
color: #17b3a3;
font-weight: 500;
margin-bottom: 6px;
}
.action-group .el-input {
height: 32px;
}
.action-group .el-button {
height: 32px;
font-size: 12px;
padding: 8px 12px;
}
.pack-list {
margin-top: 8px;
}
.pack-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px;
margin-bottom: 6px;
background: white;
padding: 4px 8px;
border-radius: 4px; border-radius: 4px;
border: 1px solid #e0e0e0;
font-size: 13px;
} }
.status-ok {
color: #67C23A;
font-weight: bold;
font-size: 16px;
}
.status-wait {
color: #E6A23C;
font-weight: bold;
/* 扫描框 */
.scan-box input {
width: 100%;
padding: 12px;
font-size: 16px; font-size: 16px;
} }
.delete-btn {
color: #F56C6C;
padding: 0;
font-size: 12px;
/* 列表 */
.item-list {
flex: 1;
overflow-y: auto;
margin: 10px 0;
border: 1px solid rgba(200, 200, 200, 0.8);
} }
/* 提交按钮 */
.submit-btn {
width: calc(100% - 32px);
margin: 16px 16px 12px 16px;
height: 44px;
font-size: 16px;
background: #17b3a3;
border-color: #17b3a3;
border-radius: 6px;
.item-list span {
color: #000;
font-size: 15px;
} }
.submit-btn:hover {
background: #15a093;
border-color: #15a093;
.bottom-line-row {
border-bottom: 1px solid rgba(200, 200, 200, 0.8);
} }
/* PDA适配 - 增大触摸目标 */
.el-input__inner {
height: 44px;
.recvButton {
font-size: 16px; font-size: 16px;
padding: 0 15px;
border-radius: 3px;
color: #17b3a3;
} }
.el-button {
min-height: 40px;
padding: 10px 16px;
font-size: 14px;
.item-list .el-row {
cursor: pointer;
transition: background 0.3s;
} }
.el-button--primary {
background-color: #17b3a3;
border-color: #17b3a3;
.item-list .el-row:hover {
background: #f5f7fa;
} }
.el-button--primary:hover {
background-color: #15a093;
border-color: #15a093;
/* 表单区域 */
.form-section >>> .el-col {
margin-bottom: 2px;
} }
.el-button--danger {
background-color: #17b3a3;
border-color: #17b3a3;
/* 自定义loading样式 */
.pda-container >>> .el-loading-mask {
background-color: rgba(255, 255, 255, 0.3) !important;
} }
.el-button--danger:hover {
background-color: #15a093;
border-color: #15a093;
.pda-container >>> .el-loading-spinner {
margin-top: -25px;
} }
/* 表单项间距优化 */
.el-form-item {
margin-bottom: 12px;
.pda-container >>> .el-loading-spinner .circular {
width: 35px;
height: 35px;
} }
.el-form-item__label {
.pda-container >>> .el-loading-text {
color: #17b3a3 !important;
font-size: 14px; font-size: 14px;
color: #333;
font-weight: 500; font-weight: 500;
}
/* 主内容区域 */
.main-content {
padding-bottom: 12px;
margin-top: 10px;
} }
</style> </style>
Loading…
Cancel
Save