Browse Source

MR修改

master
shenzhouyu 4 months ago
parent
commit
53e1d6bbea
  1. 8
      src/api/mr/mr.js
  2. 4
      src/router/index.js
  3. 3
      src/views/modules/mr-issue/mrPicking.vue
  4. 2
      src/views/modules/mr-issue/mrPickingDetail.vue
  5. 544
      src/views/modules/mr-issue/mrPickingReturn.vue
  6. 144
      src/views/modules/mr-issue/mrReturnIssueList.vue
  7. 1607
      src/views/modules/mr-issue/mrReturnPickingDetail.vue

8
src/api/mr/mr.js

@ -10,4 +10,10 @@ export const getMrMaterialLines = data => createAPI(`/pda/mr/getMrMaterialLines`
export const scanMaterialLabel = data => createAPI(`/pda/mr/scanMaterialLabel`, 'post', data)
// 确认MR发料
export const confirmMrIssue = data => createAPI(`mr/confirmMrIssue`, 'post', data)
export const confirmMrIssue = data => createAPI(`/pda/mr/confirmMrIssue`, 'post', data)
export const getReturnMrInfo = data => createAPI(`/pda/mr/getReturnMrInfo`, 'post', data)
export const getMrMaterials = data => createAPI(`/pda/mr/getMrMaterials`, 'post', data)
export const getIssueForMaterialRequisition = data => createAPI(`/pda/mr/getIssueForMaterialRequisition`, 'post', data)

4
src/router/index.js

@ -95,6 +95,10 @@ const globalRoutes = [
{path: "/mrPicking",name: "mrPicking", component: resolve => require(["@/views/modules/mr-issue/mrPicking.vue"], resolve), meta: { transition: 'instant' ,preload: true,keepAlive: true}},
{path: "/mrPickingDetail/:orderNo/:lineNo/:partNo/:material",name: "mrPickingDetail", component: resolve => require(["@/views/modules/mr-issue/mrPickingDetail.vue"], resolve), meta: { transition: 'instant' ,preload: true,keepAlive: true}},
// MR退料
{path: "/mrPickingReturn", name: "mrPickingReturn", component: resolve => require(["@/views/modules/mr-issue/mrPickingReturn.vue"], resolve), meta: { transition: 'instant', preload: true, keepAlive: true } },
{path: "/mrReturnIssueList", name: "mrReturnIssueList", component: resolve => require(["@/views/modules/mr-issue/mrReturnIssueList.vue"], resolve), meta: { transition: 'instant', preload: true, keepAlive: true } },
{path: "/mrReturnPickingDetail/:orderNo/:orderType/:partNo/:transactionId/:quantity/:batchNo", name: "mrReturnPickingDetail", component: resolve => require(["@/views/modules/mr-issue/mrReturnPickingDetail.vue"], resolve), meta: { transition: 'instant', preload: true, keepAlive: true } },
// 盘点
{path: "/stocktaking",name: "stocktaking", component: resolve => require(["@/views/modules/inventory/index.vue"], resolve), meta: { transition: 'instant' ,preload: true,keepAlive: true}},

3
src/views/modules/mr-issue/mrPicking.vue

@ -30,6 +30,9 @@
<div class="part-desc-row">
<span class="desc-text">目的地:{{ item.intDestDesc }}</span>
</div>
<div class="part-desc-row">
<span class="desc-text">状态:{{ item.statusCode }}</span>
</div>
<!-- <div class="card-details">
<div class="detail-item">

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

@ -308,7 +308,7 @@ export default {
issueQty: this.totalIssueQty,
selectedMaterials: this.labelList.map((label) => ({
labelCode: label.labelCode,
quantity: label.quantity,
issueQty: label.quantity,
batchNo: label.batchNo,
partNo: label.partNo,
locationId: label.locationId,

544
src/views/modules/mr-issue/mrPickingReturn.vue

@ -0,0 +1,544 @@
<template>
<div class="pda-container">
<!-- 头部栏 -->
<div class="header-bar">
<div class="header-left" @click="$router.back()">
<i class="el-icon-arrow-left"></i>
<span>MR退料</span>
</div>
<div class="header-right" @click="$router.push({ path: '/' })">
首页
</div>
</div>
<!-- MR号输入 -->
<div class="search-container">
<el-input
clearable
v-model="orderNo"
placeholder="请输入MR号"
prefix-icon="el-icon-search"
@keyup.enter.native="handleSearchMr"
ref="mrInput"
/>
</div>
<!-- MR信息卡片列表 -->
<div class="work-order-list" v-if="mrList.length > 0">
<div
v-for="(mr, index) in displayMrList"
:key="index"
:class="['work-order-card', { selected: selectedMr && isSameMr(selectedMr, mr) }]"
@click="selectMr(mr)"
>
<div class="card-title">
<span class="title-label">MR号{{ mr.orderNo }} &nbsp;&nbsp; 客户号{{ mr.intCustomerNo }}</span>
<span class="title-value">{{ mr.partNo }}</span>
</div>
<!-- 物料描述单独一行 -->
<div class="part-desc-row">
<span class="desc-text">目的地:{{ mr.intDestDesc }}</span>
</div>
<div class="part-desc-row">
<span class="desc-text">状态:{{ mr.statusCode }}</span>
</div>
<!-- <div class="card-details">
<div class="detail-item">
<div class="detail-label">需求数量</div>
<div class="detail-value">{{ mr.qtyRequired }}</div>
</div>
<div class="detail-item">
<div class="detail-label">已发数量</div>
<div class="detail-value">{{ mr.qtyIssued || 0 }}</div>
</div>
<div class="detail-item">
<div class="detail-label">单位</div>
<div class="detail-value">{{ mr.uom || "个" }}</div>
</div>
</div> -->
</div>
</div>
<!-- 材料列表 -->
<div
class="content-area"
v-if="selectedMr && materialList.length > 0"
>
<div
v-for="(material, index) in materialList"
:key="index"
class="material-card"
@click="openIssueList(material)"
>
<div class="card-title">
<span class="title-label"
>物料编码{{ material.partNo }} &nbsp;&nbsp; 行号{{ material.lineNo
}}</span
>
</div>
<!-- 物料描述单独一行 -->
<div class="part-desc-row">
<span class="desc-text">{{ material.partDesc }}</span>
</div>
<div class="card-details">
<div class="detail-item">
<div class="detail-label">退回数量</div>
<div class="detail-value">{{ material.qtyReturned }}</div>
</div>
<div class="detail-item">
<div class="detail-label">已发数量</div>
<div class="detail-value">{{ material.qtyIssued || 0 }}</div>
</div>
<div class="detail-item">
<div class="detail-label">单位</div>
<div class="detail-value">{{ material.unitMeas }}</div>
</div>
</div>
</div>
</div>
<!-- 空状态 -->
<div
v-if="selectedMr && materialList.length === 0"
class="empty-state"
>
<i class="el-icon-box"></i>
<p>该MR暂无材料清单</p>
</div>
<!-- 加载状态 -->
<div v-if="loading" class="loading-state">
<i class="el-icon-loading"></i>
<p>加载中...</p>
</div>
</div>
</template>
<script>
import { getMrMaterials,getReturnMrInfo } from '@/api/mr/mr';
import moment from 'moment';
export default {
data() {
return {
orderNo: "",
mrList: [],
selectedMr: null,
materialList: [],
selectedMaterial: null,
loading: false,
showOnlySelected: false,
};
},
computed: {
displayMrList() {
if (this.showOnlySelected && this.selectedMr) {
return [this.selectedMr]
}
return this.mrList
},
},
methods: {
formatDate(date) {
return date ? moment(date).format('YYYY-MM-DD') : '';
},
// MR
handleSearchMr() {
if (!this.orderNo.trim()) {
this.$message.warning("请输入MR号");
return;
}
this.loading = true;
const params = {
orderNo: this.orderNo.trim(),
site: this.$store.state.user.site,
};
getReturnMrInfo(params).then(({ data }) => {
this.loading = false;
if (
data.list &&
data.list.length > 0 &&
data.code === 0
) {
this.mrList = data.list;
this.selectedMr = null;
this.materialList = [];
this.showOnlySelected = false;
} else {
this.$message.error("未找到该MR信息");
this.mrList = [];
this.selectedMr = null;
this.materialList = [];
this.showOnlySelected = false;
}
})
.catch((error) => {
this.loading = false;
console.error("查询MR信息失败:", error);
this.$message.error("查询MR信息失败");
});
},
// MR
isSameMr(a, b) {
if (!a || !b) return false
return a.orderNo === b.orderNo && a.lineNo === b.lineNo
},
// MR
selectMr(mr) {
if (
this.showOnlySelected &&
this.selectedMr &&
this.isSameMr(this.selectedMr, mr)
) {
//
this.selectedMr = null
this.materialList = []
this.showOnlySelected = false
return
}
// MR
this.selectedMr = mr
this.showOnlySelected = true
this.loadMaterialList()
},
// MR BOM
loadMaterialList() {
if (!this.selectedMr) {
this.materialList = [];
return;
}
const params = {
orderNo: this.selectedMr.orderNo,
site: this.$store.state.user.site,
};
getMrMaterials(params)
.then(({ data }) => {
if (data && data.code === 0) {
this.materialList = (data.list || []).map((item, index) => ({
...item,
id: index + 1,
}));
} else {
this.$message.error(data.msg || '获取材料清单失败');
this.materialList = [];
}
})
.catch((error) => {
console.error('获取材料清单失败:', error);
this.$message.error('获取材料清单失败');
});
},
// 退
openIssueList(material) {
this.$router.push({
name: 'mrReturnIssueList',
params: {
orderNo: this.selectedMr.orderNo,
partNo: material.partNo,
},
});
},
},
mounted() {
// MR
this.$nextTick(() => {
if (this.$refs.mrInput) {
this.$refs.mrInput.focus();
}
});
},
};
</script>
<style scoped>
.pda-container {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
background: #f5f5f5;
}
/* 头部栏 */
.header-bar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 16px;
background: #17B3A3;
color: white;
height: 40px;
min-height: 40px;
}
.header-left {
display: flex;
align-items: center;
cursor: pointer;
font-size: 16px;
font-weight: 500;
}
.header-left i {
margin-right: 8px;
font-size: 18px;
}
.header-right {
cursor: pointer;
font-size: 16px;
font-weight: 500;
}
/* 搜索容器 */
.search-container {
padding: 12px 16px;
background: white;
display: flex;
align-items: center;
gap: 12px;
}
.search-container .el-input {
flex: 1;
}
/* MR列表 */
.work-order-list {
overflow-y: auto;
padding: 12px 16px;
}
/* MR卡片 */
.work-order-card {
background: white;
border-radius: 8px;
margin-bottom: 12px;
padding: 16px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
cursor: pointer;
transition: all 0.2s ease;
border: 2px solid transparent;
}
.work-order-card:hover {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
transform: translateY(-1px);
}
.work-order-card.selected {
border: 2px solid #17b3a3;
background: #f0fffe;
}
.close-icon {
color: #17b3a3;
font-size: 16px;
margin-left: 8px;
cursor: pointer;
float: right;
}
.close-icon:hover {
color: #14a085;
}
.work-order-card:active {
transform: translateY(0);
}
/* 内容区域 */
.content-area {
flex: 1;
overflow-y: auto;
padding: 12px 16px;
}
/* 材料卡片 */
.material-card {
background: white;
border-radius: 8px;
margin-bottom: 12px;
padding: 16px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
cursor: pointer;
transition: all 0.2s ease;
border: 2px solid transparent;
}
.material-card:hover {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
transform: translateY(-1px);
}
.material-card.selected {
border-color: #17b3a3;
background: #f0fffe;
}
.material-card:active {
transform: translateY(0);
}
/* 卡片标题 */
.card-title {
margin-bottom: 12px;
}
.title-label {
font-size: 12px;
color: #666;
display: block;
margin-bottom: 4px;
}
.title-value {
font-size: 16px;
font-weight: bold;
color: #333;
margin-left: 16px;
}
/* 物料描述行 */
.part-desc-row {
margin-bottom: 12px;
padding: 0 4px;
}
.desc-text {
font-size: 12px;
color: #666;
line-height: 1.3;
word-break: break-all;
}
/* 卡片详情 */
.card-details {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 4px;
}
.detail-item {
flex: 1;
text-align: center;
min-width: 50px;
max-width: 70px;
}
.detail-label {
font-size: 11px;
color: #666;
margin-bottom: 4px;
line-height: 1.2;
margin-left: -12px;
}
.detail-value {
font-size: 13px;
color: #333;
line-height: 1.2;
margin-left: -12px;
}
/* 空状态 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 60px 20px;
color: #999;
}
.empty-state i {
font-size: 48px;
margin-bottom: 16px;
}
.empty-state p {
font-size: 14px;
margin: 0;
}
/* 加载状态 */
.loading-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 60px 20px;
color: #17B3A3;
}
.loading-state i {
font-size: 24px;
margin-bottom: 12px;
animation: spin 1s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.loading-state p {
font-size: 14px;
margin: 0;
}
/* 响应式设计 */
@media (max-width: 360px) {
.header-bar {
padding: 8px 12px;
}
.search-container {
padding: 8px 12px;
}
.work-order-list {
padding: 8px 12px;
}
.work-order-card {
padding: 12px;
}
.content-area {
padding: 8px 12px;
}
.material-card {
padding: 12px;
}
.card-details {
flex-wrap: wrap;
gap: 6px;
}
.detail-item {
flex: 0 0 48%;
margin-bottom: 6px;
min-width: 50px;
}
}
</style>

144
src/views/modules/mr-issue/mrReturnIssueList.vue

@ -0,0 +1,144 @@
<template>
<div class="pda-container">
<!-- 头部栏 -->
<div class="header-bar">
<div class="header-left" @click="$router.back()">
<i class="el-icon-arrow-left"></i>
<span>退料 - 发料记录</span>
</div>
<div class="header-right" @click="$router.push({ path: '/' })">首页</div>
</div>
<div class="content-area">
<div class="work-order-list" v-if="issueList.length > 0">
<div
v-for="(item, index) in issueList"
:key="index"
class="material-card"
@click="goDetail(item)"
>
<div class="card-title">
<span class="title-label">
物料编码{{ partNo }} &nbsp;&nbsp; 发料号{{ item.TRANSACTION_ID }}
</span>
</div>
<div class="part-desc-row">
<span class="desc-text">批次号{{ item.LOT_BATCH_NO || '-' }}</span>
</div>
<div class="card-details">
<div class="detail-item">
<div class="detail-label">发料数量</div>
<div class="detail-value">{{ item.QUANTITY }}</div>
</div>
<div class="detail-item">
<div class="detail-label">预留数量</div>
<div class="detail-value">{{ item.QTY_REVERSED || 0 }}</div>
</div>
<div class="detail-item">
<div class="detail-label">单位</div>
<div class="detail-value">{{ item.uom || '个' }}</div>
</div>
</div>
</div>
</div>
<div v-if="!loading && issueList.length === 0" class="empty-state">
<i class="el-icon-box"></i>
<p>暂无发料记录</p>
</div>
<div v-if="loading" class="loading-state">
<i class="el-icon-loading"></i>
<p>加载中...</p>
</div>
</div>
</div>
</template>
<script>
import { getIssueForMaterialRequisition } from "@/api/mr/mr";
export default {
data() {
return {
orderNo: '',
partNo: '',
loading: false,
issueList: [],
};
},
methods: {
loadIssueList() {
console.log('加载发料记录', this.orderNo, this.partNo);
if (!this.orderNo || !this.partNo) {
return;
}
this.loading = true;
const params = {
orderNo: '8715',
site: this.$store.state.user.site,
partNo: this.partNo,
};
getIssueForMaterialRequisition(params)
.then(({ data }) => {
this.loading = false;
if (data && data.code === 0) {
this.issueList = data.mrIssueList || [];
} else {
this.$message.error(data.msg || '获取发料记录失败');
this.issueList = [];
}
})
.catch(() => {
this.loading = false;
this.$message.error('获取发料记录失败');
});
},
goDetail(item) {
this.$router.push({
name: 'mrReturnPickingDetail',
params: {
orderNo: this.orderNo,
orderType: 'mr',
partNo: this.partNo,
transactionId: item.TRANSACTION_ID,
quantity: item.QUANTITY,
batchNo: item.LOT_BATCH_NO,
},
});
},
},
mounted() {
this.orderNo = this.$route.params.orderNo;
this.partNo = this.$route.params.partNo;
this.loadIssueList();
},
};
</script>
<style scoped>
.pda-container { width: 100vw; height: 100vh; display: flex; flex-direction: column; background: #f5f5f5; }
.header-bar { display: flex; justify-content: space-between; align-items: center; padding: 8px 16px; background: #17B3A3; color: white; height: 40px; min-height: 40px; }
.header-left { display: flex; align-items: center; cursor: pointer; font-size: 16px; font-weight: 500; }
.header-left i { margin-right: 8px; font-size: 18px; }
.header-right { cursor: pointer; font-size: 16px; font-weight: 500; }
.content-area { flex: 1; overflow-y: auto; padding: 12px 16px; }
.work-order-list { overflow-y: auto; padding: 12px 16px; }
.material-card { background: white; border-radius: 8px; margin-bottom: 12px; padding: 16px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); cursor: pointer; transition: all 0.2s ease; border: 2px solid transparent; }
.material-card:hover { box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); transform: translateY(-1px); }
.material-card:active { transform: translateY(0); }
.card-title { margin-bottom: 12px; }
.title-label { font-size: 12px; color: #666; display: block; margin-bottom: 4px; }
.part-desc-row { margin-bottom: 12px; padding: 0 4px; }
.desc-text { font-size: 12px; color: #666; line-height: 1.3; word-break: break-all; }
.card-details { display: flex; justify-content: space-between; align-items: flex-start; gap: 4px; }
.detail-item { flex: 1; text-align: center; min-width: 50px; max-width: 70px; }
.detail-label { font-size: 11px; color: #666; margin-bottom: 4px; line-height: 1.2; margin-left: -12px; }
.detail-value { font-size: 13px; color: #333; line-height: 1.2; margin-left: -12px; }
.empty-state { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 60px 20px; color: #999; }
.empty-state i { font-size: 48px; margin-bottom: 16px; }
.loading-state { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 60px 20px; color: #17B3A3; }
.loading-state i { font-size: 24px; margin-bottom: 12px; animation: spin 1s linear infinite; }
@keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
</style>

1607
src/views/modules/mr-issue/mrReturnPickingDetail.vue
File diff suppressed because it is too large
View File

Loading…
Cancel
Save