Browse Source

直接发料

master
shenzhouyu 4 months ago
parent
commit
376db80153
  1. 8
      src/api/production/production-issue.js
  2. 4
      src/router/index.js
  3. 46
      src/views/modules/production-inbound/production.vue
  4. 756
      src/views/modules/production-issue/directIssue.vue
  5. 568
      src/views/modules/production-issue/directIssueDetail.vue
  6. 287
      src/views/modules/production-issue/index.vue
  7. 10
      src/views/modules/production-issue/production.vue
  8. 1362
      src/views/modules/production-issue/productionIssuePda.vue
  9. 106
      src/views/modules/production-return/pick.vue
  10. 412
      src/views/modules/production-return/productionReturnPicking.vue

8
src/api/production/production-issue.js

@ -32,3 +32,11 @@ export const getIssureNotifyByNo = data => createAPI(`/pda/production/issue/getI
// 获取工单列表
export const getIssureNotifyListByNo = data => createAPI(`/pda/production/issue/getIssureNotifyListByNo`,'post',data)
// 直接领料相关接口
// 获取工单信息
export const getWorkOrderInfo = data => createAPI(`/pda/production/issue/getWorkOrderInfo`,'post',data)
// 扫描材料标签(直接领料)
export const scanMaterialLabelDirect = data => createAPI(`/pda/production/issue/scanMaterialLabelDirect`,'post',data)
// 确认直接发料
export const confirmDirectIssue = data => createAPI(`/pda/production/issue/confirmDirectIssue`,'post',data)

4
src/router/index.js

@ -46,9 +46,12 @@ const globalRoutes = [
{path: "/productionissue",name: "productionissue", component: resolve => require(["@/views/modules/production-issue/production.vue"], resolve), meta: { transition: 'instant' ,preload: true,keepAlive: true}},
{ path: "/productionPicking", name: "productionPicking", component: resolve => require(["@/views/modules/production-issue/productionPicking.vue"], resolve), meta: { transition: 'instant', preload: true, keepAlive: true } },
{ path: "/productionPickingDetail/:outboundNo", name: "productionPickingDetail", component: resolve => require(["@/views/modules/production-issue/productionPickingDetail.vue"], resolve), meta: { transition: 'instant', preload: true, keepAlive: true } },
{ path: "/directIssue", name: "directIssue", component: resolve => require(["@/views/modules/production-issue/directIssue.vue"], resolve), meta: { transition: 'instant', preload: true, keepAlive: true } },
{ path: "/directIssueDetail/:workOrderNo/:partNo/:partDesc/:requiredQty/:issuedQty", name: "directIssueDetail", component: resolve => require(["@/views/modules/production-issue/directIssueDetail.vue"], resolve), meta: { transition: 'instant', preload: true, keepAlive: true } },
//生产退料
{path: "/productionreturn",name: "productionreturn", component: resolve => require(["@/views/modules/production-return/production.vue"], resolve), meta: { transition: 'instant' ,preload: true,keepAlive: true}},
{path: "/productionReturnPicking", name: "productionReturnPicking", component: resolve => require(["@/views/modules/production-return/productionReturnPicking.vue"], resolve), meta: { transition: 'instant', preload: true, keepAlive: true } },
{path: "/productionReturnPickingDetail/:orderNo/:orderType", name: "productionReturnPickingDetail", component: resolve => require(["@/views/modules/production-return/productionReturnPickingDetail.vue"], resolve), meta: { transition: 'instant', preload: true, keepAlive: true } },
{path: '/production-return/pick/:orderNo',
name: 'ProductionIssuePick',component: resolve => require(["@/views/modules/production-return/pick.vue"], resolve),
meta: { transition: 'instant' ,preload: true,keepAlive: true}},
@ -60,7 +63,6 @@ const globalRoutes = [
{path: "/inboundRegisterDetail/:inboundNo",name: "inboundRegisterDetail", component: resolve => require(["@/views/modules/production-inbound/inboundRegisterDetail.vue"], resolve), meta: { transition: 'instant' ,preload: true,keepAlive: true}},
// 委外
{path: "/outsource",name: "outsource", component: resolve => require(["@/views/modules/outsourcing-issue/index.vue"], resolve), meta: { transition: 'instant' ,preload: true,keepAlive: true}},
{ path: "/outsourcingPicking", name: "outsourcingPicking", component: resolve => require(["@/views/modules/outsourcing-issue/outsourcingPicking.vue"], resolve), meta: { transition: 'instant', preload: true, keepAlive: true } },
{ path: "/outsourcingPickingDetail/:outsourcingNo", name: "outsourcingPickingDetail", component: resolve => require(["@/views/modules/outsourcing-issue/outsourcingPickingDetail.vue"], resolve), meta: { transition: 'instant', preload: true, keepAlive: true } },

46
src/views/modules/production-inbound/production.vue

@ -5,9 +5,7 @@
<i class="el-icon-arrow-left"></i>
<span>生产订单入库</span>
</div>
<div class="header-right" @click="$router.push({ path: '/' })">
首页
</div>
<div class="header-right" @click="$router.push({ path: '/' })">首页</div>
</div>
<!-- 功能菜单 -->
@ -16,7 +14,7 @@
class="menu-item"
v-for="(btn, index) in buttons"
:key="index"
:class="{ 'disabled': btn.disabled }"
:class="{ disabled: btn.disabled }"
@click="handleButtonClick(btn)"
>
<div class="menu-icon" :class="btn.iconClass">
@ -33,22 +31,40 @@ export default {
data() {
return {
buttons: [
{ icon: 'scan', label: '包装', iconClass: 'purchase', to: 'productionPick', disabled: false },
{ icon: 'records', label: '装托盘', iconClass: 'qualified', to: 'productionPick', disabled: true },
{ icon: 'box', label: '入库登记', iconClass: 'inspection', to: 'inboundRegister', disabled: false },
]
}
{
icon: "scan",
label: "包装",
iconClass: "purchase",
to: "productionPick",
disabled: false,
},
{
icon: "records",
label: "装托盘",
iconClass: "qualified",
to: "productionPick",
disabled: true,
},
{
icon: "box",
label: "入库登记",
iconClass: "inspection",
to: "inboundRegister",
disabled: false,
},
],
};
},
methods: {
handleButtonClick(btn) {
if (btn.disabled) {
this.$message.warning('正在开发中,敬请期待...');
this.$message.warning("正在开发中,敬请期待...");
} else {
this.$router.push(btn.to);
}
}
}
}
},
},
};
</script>
<style>
@ -63,7 +79,7 @@ export default {
justify-content: space-between;
align-items: center;
padding: 8px 16px;
background: #17B3A3;
background: #17b3a3;
color: white;
height: 40px;
min-height: 40px;
@ -119,7 +135,7 @@ export default {
}
.menu-item.disabled::after {
content: '开发中';
content: "开发中";
position: absolute;
top: 8px;
right: 8px;

756
src/views/modules/production-issue/directIssue.vue

@ -0,0 +1,756 @@
<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="search-container">
<el-input
clearable
v-model="workOrderNo"
placeholder="请输入工单号"
prefix-icon="el-icon-search"
@keyup.enter.native="handleSearchWorkOrder"
ref="workOrderInput"
/>
</div>
<!-- 工单信息卡片列表 -->
<div class="work-order-list" v-if="workOrderList.length > 0">
<div
v-for="(workOrder, index) in workOrderList"
:key="index"
class="work-order-card"
@click="selectWorkOrder(workOrder)"
>
<div class="card-title">
<span class="title-label">工单号{{ workOrder.orderNo }}</span>
<span class="title-value">{{ workOrder.partNo }}</span>
</div>
<!-- 物料描述单独一行 -->
<div class="part-desc-row">
<span class="desc-text">{{ workOrder.partDesc }}</span>
</div>
<div class="card-details">
<div class="detail-item">
<div class="detail-label">计划数量</div>
<div class="detail-value">{{ workOrder.qtyComplete }}</div>
</div>
<div class="detail-item">
<div class="detail-label">状态</div>
<div class="detail-value">{{ workOrder.status || "进行中" }}</div>
</div>
<div class="detail-item">
<div class="detail-label">单位</div>
<div class="detail-value">{{ workOrder.uom || "个" }}</div>
</div>
</div>
</div>
</div>
<!-- 材料列表 -->
<div
class="content-area"
v-if="selectedWorkOrder && materialList.length > 0"
>
<div
v-for="(material, index) in materialList"
:key="index"
class="material-card"
@click="selectMaterial(material)"
>
<div class="card-title">
<span class="title-label"
>物料编码{{ material.componentPartNo }} &nbsp;&nbsp; 行号{{
material.lineNo || index + 1
}}</span
>
<!-- <span class="title-value">{{ material.componentPartNo }}</span> -->
</div>
<!-- 物料描述单独一行 -->
<div class="part-desc-row">
<span class="desc-text">{{ material.componentPartDesc }}</span>
</div>
<div class="card-details">
<div class="detail-item">
<div class="detail-label">需求数量</div>
<div class="detail-value">{{ material.qtyRequired }}</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.uom || "个" }}</div>
</div>
</div>
</div>
</div>
<!-- 空状态 -->
<div
v-if="selectedWorkOrder && materialList.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>
</template>
<script>
import {
getWorkOrderInfo,
getWorkOrderMaterials,
} from "@/api/production/production-issue";
import moment from "moment";
export default {
data() {
return {
workOrderNo: "",
workOrderList: [],
selectedWorkOrder: null,
materialList: [],
selectedMaterial: null,
loading: false,
};
},
methods: {
formatDate(date) {
return date ? moment(date).format("YYYY-MM-DD") : "";
},
//
handleSearchWorkOrder() {
if (!this.workOrderNo.trim()) {
this.$message.warning("请输入工单号");
return;
}
this.loading = true;
const params = {
workOrderNo: this.workOrderNo.trim(),
site: this.$store.state.user.site,
};
getWorkOrderInfo(params)
.then(({ data }) => {
this.loading = false;
console.log("工单信息", data);
if (
data.workOrders &&
data.workOrders.length > 0 &&
data.code === 0
) {
this.workOrderList = data.workOrders;
this.selectedWorkOrder = null;
this.materialList = [];
} else {
this.$message.error("未找到该工单信息");
this.workOrderList = [];
this.selectedWorkOrder = null;
this.materialList = [];
}
})
.catch((error) => {
this.loading = false;
console.error("查询工单信息失败:", error);
this.$message.error("查询工单信息失败");
});
},
//
selectWorkOrder(workOrder) {
this.selectedWorkOrder = workOrder;
this.loadMaterialList();
},
//
loadMaterialList() {
if (!this.selectedWorkOrder) {
this.materialList = [];
return;
}
const params = {
workOrderNo: this.selectedWorkOrder.orderNo,
site: this.$store.state.user.site,
};
getWorkOrderMaterials(params)
.then(({ data }) => {
console.log("材料清单", data);
if (data && data.code === 0) {
this.materialList = (data.materials || []).map((item, index) => ({
...item,
id: index + 1,
}));
} else {
this.$message.error(data.msg || "获取材料清单失败");
this.materialList = [];
}
})
.catch((error) => {
console.error("获取材料清单失败:", error);
this.$message.error("获取材料清单失败");
});
},
//
selectMaterial(material) {
console.log("选择材料", material);
if(material.reserveIssueMethod != 'Reserve And Backflush'){
this.$message.warning('该物料为'+material.reserveIssueMethod+',不支持直接领料,请选择其他物料!');
return;
}
this.$router.push({
name: "directIssueDetail",
params: {
workOrderNo: this.selectedWorkOrder.orderNo,
partNo: material.componentPartNo,
partDesc: material.componentPartDesc,
requiredQty: material.qtyRequired,
issuedQty: material.qtyIssued || 0,
},
});
},
},
mounted() {
//
this.$nextTick(() => {
if (this.$refs.workOrderInput) {
this.$refs.workOrderInput.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;
}
.search-btn {
padding: 8px 16px;
background: #17b3a3;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: background-color 0.2s;
}
.search-btn:hover {
background: #0d8f7f;
}
.search-btn:disabled {
background: #ccc;
cursor: not-allowed;
}
/* 工单列表 */
.work-order-list {
overflow-y: auto;
padding: 12px 16px;
}
/* 工单卡片 */
.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-color: #17b3a3;
background: #f0fffe;
}
.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;
}
/* 扫描区域 */
.scan-section {
margin-top: 16px;
}
/* 扫描容器 */
.scan-container {
padding: 12px 16px;
background: white;
display: flex;
align-items: center;
gap: 12px;
border-radius: 0 0 8px 8px;
margin-bottom: 12px;
}
.scan-container .el-input {
width: 240px;
margin-right: 12px;
}
/* 紧凑型输入框样式 */
.compact-input ::v-deep .el-input__inner {
height: 36px;
padding: 0 12px 0 35px;
font-size: 14px;
}
.compact-input ::v-deep .el-input__prefix {
left: 10px;
}
.compact-input ::v-deep .el-input__suffix {
right: 30px;
}
/* 模式切换开关 */
.mode-switch {
position: relative;
display: inline-block;
}
.custom-switch {
transform: scale(1.3);
}
/* 中间文字 */
.switch-text {
position: absolute;
left: 25%;
top: 53%;
transform: translate(-50%, -50%);
font-size: 12px;
font-weight: bold;
color: white;
pointer-events: none;
z-index: 2;
}
.switch-text2 {
position: absolute;
left: 75%;
top: 53%;
transform: translate(-50%, -50%);
font-size: 12px;
font-weight: bold;
color: white;
pointer-events: none;
z-index: 2;
}
/* 调整 switch 尺寸以便容纳文字 */
.custom-switch ::v-deep .el-switch__core {
width: 60px;
height: 28px;
}
/* 已选材料信息 */
.selected-material-info {
background: #e8f5e8;
padding: 12px 16px;
margin-bottom: 12px;
border-radius: 8px;
border-left: 4px solid #17b3a3;
}
.info-title {
font-size: 14px;
font-weight: bold;
color: #333;
margin-bottom: 8px;
}
.info-details {
display: flex;
gap: 16px;
font-size: 12px;
color: #666;
}
/* 标签列表 */
.label-list {
background: white;
margin-bottom: 12px;
border-radius: 8px;
overflow: hidden;
}
.list-header {
display: flex;
background: #f8f9fa;
padding: 12px 8px;
border-bottom: 1px solid #e0e0e0;
font-size: 12px;
color: #666;
font-weight: 500;
}
.list-item {
display: flex;
padding: 12px 8px;
border-bottom: 1px solid #f0f0f0;
font-size: 12px;
color: #333;
}
.list-item:last-child {
border-bottom: none;
}
.col-no {
width: 30px;
text-align: center;
}
.col-label {
flex: 2;
text-align: center;
}
.col-batch {
flex: 1;
text-align: center;
}
.col-qty {
width: 60px;
text-align: center;
}
.empty-labels {
padding: 40px 20px;
text-align: center;
color: #999;
}
.empty-labels p {
margin: 0;
font-size: 14px;
}
/* 底部操作按钮 */
.bottom-actions {
display: flex;
padding: 16px;
gap: 20px;
background: white;
border-radius: 8px;
}
.action-btn {
flex: 1;
padding: 12px;
border: 1px solid #17b3a3;
background: white;
color: #17b3a3;
border-radius: 20px;
font-size: 14px;
cursor: pointer;
transition: all 0.2s ease;
}
.action-btn.primary {
background: #17b3a3;
color: white;
}
.action-btn.primary:hover {
background: #0d8f7f;
}
.action-btn.secondary:hover {
background: #17b3a3;
color: white;
}
.action-btn:active {
transform: scale(0.98);
}
.action-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.action-btn:disabled:hover {
background: #17b3a3;
color: white;
}
/* 空状态 */
.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;
}
.list-header,
.list-item {
font-size: 11px;
}
.col-label {
flex: 1.5;
}
}
</style>

568
src/views/modules/production-issue/directIssueDetail.vue

@ -0,0 +1,568 @@
<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="search-container">
<el-input clearable class="compact-input" v-model="scanCode" placeholder="请扫描材料纸质标签" prefix-icon="el-icon-search"
@keyup.enter.native="handleScan" ref="scanInput" />
<div class="mode-switch">
<el-switch class="custom-switch" v-model="isRemoveMode" active-color="#ff4949" inactive-color="#13ce66">
</el-switch>
<span v-if="isRemoveMode" class="switch-text">{{ '移除' }}</span>
<span v-else class="switch-text2">{{ '添加' }}</span>
</div>
</div>
<!-- 工单及物料信息 -->
<div class="work-order-list" v-if="workOrderNo && componentPartNo">
<div class="work-order-card">
<div class="card-title">
<span class="title-label">工单号{{ workOrderNo }}</span>
</div>
<!-- 物料描述单独一行 -->
<div class="part-desc-row">
<span class="desc-text">物料编码{{ componentPartNo }}</span>
</div>
<div class="part-desc-row">
<span class="desc-text">物料名称{{ componentPartDesc }}</span>
</div>
<div class="card-details">
<div class="detail-item">
<div class="detail-label">需求数量</div>
<div class="detail-value">{{ requiredQty }}</div>
</div>
<div class="detail-item">
<div class="detail-label">已发数量</div>
<div class="detail-value">{{ issuedQty }}</div>
</div>
<div class="detail-item">
<div class="detail-label">本次</div>
<div class="detail-value">{{ totalScannedQty }}</div>
</div>
</div>
</div>
</div>
<div class="section-title">
<div class="title-left">
<i class="el-icon-circle-check"></i>
<span>出库信息确认</span>
</div>
</div>
<!-- 标签列表 -->
<div class="label-list">
<div class="list-header">
<div class="col-no">NO.</div>
<div class="col-label">标签条码</div>
<div class="col-batch">仓库</div>
<div class="col-batch">批次号</div>
<div class="col-qty">数量</div>
</div>
<div v-for="(label, index) in scannedLabels" :key="label.id" class="list-item">
<div class="col-no">{{ index+1 }}</div>
<div class="col-label">{{ label.labelCode }}</div>
<div class="col-batch">{{label.warehouseId}}</div>
<div class="col-batch">{{ label.batchNo || '-' }}</div>
<div class="col-qty">{{ label.quantity }}</div>
</div>
<div v-if="scannedLabels.length === 0" class="empty-labels">
<p>暂无扫描标签</p>
</div>
</div>
<!-- 底部操作按钮 -->
<div class="bottom-actions">
<button class="action-btn secondary" @click="confirmIssue" :disabled="scannedLabels.length === 0">
确定
</button>
<button class="action-btn secondary" style="margin-left: 10px;">
打印
</button>
<button class="action-btn secondary" style="margin-left: 10px;" @click="clearScannedLabels">
清空
</button>
</div>
</div>
</template>
<script>
import {
scanMaterialLabel,
confirmDirectIssue,
} from '@/api/production/production-issue'
import moment from 'moment'
export default {
data() {
return {
scanCode: '',
isRemoveMode: false,
scannedLabels: [],
workOrderNo: '',
componentPartNo: '',
componentPartDesc: '',
requiredQty: 0,
issuedQty: 0,
}
},
computed: {
totalScannedQty() {
return this.scannedLabels.reduce((sum, l) => sum + (l.quantity || 0), 0)
},
},
methods: {
formatDate(date) {
return date ? moment(date).format('YYYY-MM-DD') : ''
},
handleScan() {
if (!this.scanCode.trim()) return
if (this.isRemoveMode) {
this.removeLabelByCode(this.scanCode.trim())
} else {
this.validateAndAddLabel(this.scanCode.trim())
}
this.scanCode = ''
},
validateAndAddLabel(labelCode) {
const params = {
labelCode,
workOrderNo: this.workOrderNo,
componentPartNo: this.componentPartNo,
site: this.$store.state.user.site,
}
scanMaterialLabel(params)
.then(({ data }) => {
if (data && data.code === 0) {
const exists = this.scannedLabels.find(
(item) => item.labelCode === labelCode
)
if (exists) {
this.$message.warning('该标签已扫描,请勿重复扫描')
return
}
/* if (data.labelInfo.materialCode !== this.materialCode) {
this.$message.error('标签物料编码与选择的材料不匹配')
return
} */
this.scannedLabels.push({
id: Date.now(),
labelCode,
componentPartNo: data.labelInfo.materialCode,
quantity: data.labelInfo.availableQty,
batchNo: data.labelInfo.batchNo,
warehouseId: data.labelInfo.warehouseId,
})
this.$message.success('扫描成功')
} else {
this.$message.error(data.msg || '标签验证失败')
}
})
.catch(() => {
this.$message.error('扫描失败')
})
},
removeLabelByCode(labelCode) {
const index = this.scannedLabels.findIndex(
(item) => item.labelCode === labelCode
)
if (index !== -1) {
this.scannedLabels.splice(index, 1)
this.$message.success('移除成功')
} else {
this.$message.warning('未找到该标签')
}
},
clearScannedLabels() {
if (this.scannedLabels.length === 0) return
this.$confirm('确定清空所有已扫描的标签吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
this.scannedLabels = []
this.$message.success('已清空')
})
.catch(() => {})
},
confirmIssue() {
if (this.scannedLabels.length === 0) {
this.$message.warning('请先扫描材料标签')
return
}
const params = {
site: this.$store.state.user.site,
workOrderNo: this.workOrderNo,
componentPartNo: this.componentPartNo,
labels: this.scannedLabels.map((l) => ({
labelCode: l.labelCode,
quantity: l.quantity,
batchNo: l.batchNo,
materialCode: l.materialCode,
})),
}
confirmDirectIssue(params)
.then(({ data }) => {
if (data && data.code === 0) {
this.$message.success('发料成功')
this.$router.back()
} else {
this.$message.error(data.msg || '发料失败')
}
})
.catch(() => {
this.$message.error('发料失败')
})
},
initFromRoute() {
this.workOrderNo = this.$route.params.workOrderNo
this.componentPartNo = this.$route.params.partNo
this.componentPartDesc = this.$route.params.partDesc || ''
this.requiredQty = Number(this.$route.params.requiredQty || 0)
this.issuedQty = Number(this.$route.params.issuedQty || 0)
/* if (!this.workOrderNo || !this.materialCode) {
this.$message.error('参数错误')
this.$router.back()
} */
},
},
mounted() {
this.initFromRoute()
this.$nextTick(() => {
if (this.$refs.scanInput) {
this.$refs.scanInput.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 {
width: 240px;
margin-right: 12px;
}
.compact-input ::v-deep .el-input__inner {
height: 36px;
padding: 0 12px 0 35px;
font-size: 14px;
}
.compact-input ::v-deep .el-input__prefix {
left: 10px;
}
.compact-input ::v-deep .el-input__suffix {
right: 30px;
}
.mode-switch {
position: relative;
display: inline-block;
}
.custom-switch {
transform: scale(1.3);
}
.switch-text {
position: absolute;
left: 25%;
transform: translateX(-50%);
top: 50%;
transform: translateY(-50%) translateX(-50%);
font-size: 12px;
font-weight: 500;
color: #606266;
white-space: nowrap;
pointer-events: none;
z-index: 1;
top: 53%;
transform: translate(-50%, -50%);
font-size: 12px;
font-weight: bold;
color: white;
pointer-events: none;
z-index: 2;
}
.switch-text2 {
position: absolute;
left: 75%;
transform: translateX(-50%);
top: 50%;
transform: translateY(-50%) translateX(-50%);
font-size: 12px;
font-weight: 500;
color: #606266;
white-space: nowrap;
pointer-events: none;
z-index: 1;
top: 53%;
transform: translate(-50%, -50%);
font-size: 12px;
font-weight: bold;
color: white;
pointer-events: none;
z-index: 2;
}
/* 调整 switch 尺寸以便容纳文字 */
.custom-switch ::v-deep .el-switch__core {
width: 60px;
height: 28px;
}
/* 工单列表 */
.work-order-list {
overflow-y: auto;
padding: 12px 16px;
}
/* 工单卡片 */
.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-color: #17b3a3;
background: #f0fffe;
}
.work-order-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: 20px;
}
.section-title {
display: flex;
align-items: center;
justify-content: space-between;
padding: 6px 8px;
background: white;
margin: 0 16px;
margin-top: 4px;
border-radius: 8px 8px 0 0;
border-bottom: 2px solid #17B3A3;
}
/* 物料描述行 */
.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;
}
.label-list {
background: white;
margin: 0 16px 12px;
border-radius: 8px;
overflow: hidden;
}
.list-header {
display: flex;
background: #f8f9fa;
padding: 12px 8px;
border-bottom: 1px solid #e0e0e0;
font-size: 12px;
color: #666;
font-weight: 500;
}
.list-item {
display: flex;
padding: 12px 8px;
border-bottom: 1px solid #f0f0f0;
font-size: 12px;
color: #333;
}
.list-item:last-child {
border-bottom: none;
}
.col-no {
width: 20px;
text-align: center;
}
.col-label {
flex: 2;
text-align: center;
}
.col-batch {
flex: 1;
text-align: center;
}
.col-qty {
width: 60px;
text-align: center;
}
.empty-labels {
padding: 40px 20px;
text-align: center;
color: #999;
}
.bottom-actions {
display: flex;
padding: 16px;
gap: 20px;
background: white;
margin-top: auto;
}
.action-btn {
flex: 1;
padding: 12px;
border: 1px solid #17b3a3;
background: white;
color: #17b3a3;
border-radius: 20px;
font-size: 14px;
cursor: pointer;
transition: all 0.2s ease;
}
.action-btn:hover {
background: #17b3a3;
color: white;
}
.action-btn:active {
transform: scale(0.98);
}
@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;
}
.card-details {
flex-wrap: wrap;
gap: 6px;
}
.detail-item {
flex: 0 0 48%;
margin-bottom: 6px;
min-width: 50px;
}
.label-list {
margin: 0 12px 8px;
}
}
</style>

287
src/views/modules/production-issue/index.vue

@ -1,287 +0,0 @@
<template>
<div>
<div class="pda-container">
<div class="status-bar">
<div class="goBack" @click="$router.back()"><i class="el-icon-arrow-left"></i>上一页</div>
<div class="goBack">生产发料</div>
<div class="network" style="color: #fff" @click="$router.push({ path: '/' })">🏠首页</div>
</div>
<div style="overflow-y: auto">
<!-- 搜索栏 -->
<div class="search-section">
<van-search
v-model="searchValue"
placeholder="请输入生产订单号"
@search="handleSearch"
@clear="handleClear"
/>
</div>
<!-- 状态筛选 -->
<div class="filter-section">
<van-tabs v-model="activeTab" @change="handleTabChange">
<van-tab title="全部" name="all" />
<van-tab title="待发料" name="0" />
<van-tab title="部分发料" name="1" />
<van-tab title="已完成" name="2" />
</van-tabs>
</div>
<!-- 生产订单列表 -->
<van-pull-refresh v-model="refreshing" @refresh="onRefresh">
<van-list
v-model="loading"
:finished="finished"
finished-text="没有更多了"
@load="onLoad"
>
<div
v-for="item in orderList"
:key="item.id"
class="order-item"
@click="handleOrderClick(item)"
>
<div class="order-header">
<div class="order-no">{{ item.orderNo }}</div>
<div class="order-status" :class="getStatusClass(item.status)">
{{ getStatusText(item.status) }}
</div>
</div>
<div class="order-info">
<div class="info-row">
<span class="label">产品编码</span>
<span class="value">{{ item.productCode }}</span>
</div>
<div class="info-row">
<span class="label">产品名称</span>
<span class="value">{{ item.productName }}</span>
</div>
<div class="info-row">
<span class="label">生产数量</span>
<span class="value">{{ item.planQuantity }}</span>
</div>
<div class="info-row">
<span class="label">工作中心</span>
<span class="value">{{ item.workCenter }}</span>
</div>
<div class="info-row">
<span class="label">计划开始</span>
<span class="value">{{ item.planStartDate }}</span>
</div>
</div>
<div class="progress-section">
<van-progress
:percentage="Math.round((item.issuedQuantity / item.totalMaterialQuantity) * 100)"
:stroke-width="6"
color="#1989fa"
/>
<div class="progress-text">发料进度{{ item.issuedQuantity }}/{{ item.totalMaterialQuantity }}</div>
</div>
</div>
</van-list>
</van-pull-refresh>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'ProductionIssue',
data() {
return {
searchValue: '',
activeTab: 'all',
refreshing: false,
loading: false,
finished: false,
orderList: [
{
id: 1,
orderNo: 'MO202401001',
productCode: 'PROD001',
productName: '成品A',
planQuantity: 100,
workCenter: '装配线1',
planStartDate: '2024-01-20',
totalMaterialQuantity: 500,
issuedQuantity: 0,
status: 0
},
{
id: 2,
orderNo: 'MO202401002',
productCode: 'PROD002',
productName: '成品B',
planQuantity: 200,
workCenter: '装配线2',
planStartDate: '2024-01-18',
totalMaterialQuantity: 800,
issuedQuantity: 400,
status: 1
},
{
id: 3,
orderNo: 'MO202401003',
productCode: 'PROD003',
productName: '成品C',
planQuantity: 150,
workCenter: '装配线1',
planStartDate: '2024-01-16',
totalMaterialQuantity: 600,
issuedQuantity: 600,
status: 2
}
]
}
},
mounted() {
this.loadData()
},
methods: {
loadData() {
this.loading = true
setTimeout(() => {
this.loading = false
this.finished = true
}, 1000)
},
onRefresh() {
this.refreshing = true
setTimeout(() => {
this.refreshing = false
this.$toast.success('刷新成功')
}, 1000)
},
onLoad() {
this.loadData()
},
handleSearch() {
this.loadData()
},
handleClear() {
this.searchValue = ''
this.loadData()
},
handleTabChange() {
this.loadData()
},
handleOrderClick(item) {
if (item.status === 0 || item.status === 1) {
this.$router.push(`/production-issue/pick/${item.orderNo}`)
} else {
this.$toast('该订单已完成发料')
}
},
getStatusText(status) {
const statusMap = {
0: '待发料',
1: '部分发料',
2: '已完成'
}
return statusMap[status] || '未知'
},
getStatusClass(status) {
const classMap = {
0: 'status-pending',
1: 'status-partial',
2: 'status-completed'
}
return classMap[status] || ''
}
}
}
</script>
<style scoped>
.production-issue-container {
min-height: 100vh;
background-color: #f7f8fa;
}
.search-section {
padding: 10px 16px;
background: white;
}
.filter-section {
background: white;
border-bottom: 1px solid #ebedf0;
}
.order-item {
background: white;
margin: 10px 16px;
border-radius: 8px;
padding: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.order-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.order-no {
font-size: 16px;
font-weight: bold;
color: #323233;
}
.order-status {
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
color: white;
}
.status-pending {
background-color: #ff976a;
}
.status-partial {
background-color: #1989fa;
}
.status-completed {
background-color: #07c160;
}
.order-info {
font-size: 14px;
margin-bottom: 12px;
}
.info-row {
display: flex;
margin-bottom: 6px;
}
.info-row:last-child {
margin-bottom: 0;
}
.label {
color: #969799;
width: 80px;
flex-shrink: 0;
}
.value {
color: #323233;
flex: 1;
}
.progress-section {
margin-top: 12px;
}
.progress-text {
font-size: 12px;
color: #646566;
margin-top: 4px;
text-align: center;
}
</style>

10
src/views/modules/production-issue/production.vue

@ -35,8 +35,8 @@ export default {
icon: "scan",
label: "直接领料",
iconClass: "purchase",
to: "productionPicking",
disabled: true,
to: "directIssue",
disabled: false,
},
{
icon: "records",
@ -45,6 +45,12 @@ export default {
to: "productionPicking",
disabled: false,
},
{
icon: "records",
label: "分拣",
iconClass: "qualified",
disabled: true,
},
],
};
},

1362
src/views/modules/production-issue/productionIssuePda.vue
File diff suppressed because it is too large
View File

106
src/views/modules/production-return/pick.vue

@ -25,10 +25,14 @@
class="material-item"
>
<div class="material-info">
<div class="material-name">{{ item.materialCode }} - {{ item.materialName }}</div>
<div class="material-name">
{{ item.materialCode }} - {{ item.materialName }}
</div>
<div class="material-spec">规格{{ item.specification }}</div>
<div class="material-location">
<van-tag type="primary" size="small">{{ item.locationCode }}</van-tag>
<van-tag type="primary" size="small">{{
item.locationCode
}}</van-tag>
<span class="stock-info">库存{{ item.stock }}</span>
</div>
<div class="material-quantity">
@ -39,7 +43,9 @@
<van-stepper
v-model="item.currentPick"
:min="0"
:max="Math.min(item.requiredQuantity - item.issuedQuantity, item.stock)"
:max="
Math.min(item.requiredQuantity - item.issuedQuantity, item.stock)
"
integer
/>
</div>
@ -94,96 +100,98 @@
<script>
export default {
name: 'ProductionIssuePick',
name: "ProductionIssuePick",
data() {
return {
orderInfo: {
orderNo: 'MO202401001',
productCode: 'PROD001',
productName: '成品A',
orderNo: "MO202401001",
productCode: "PROD001",
productName: "成品A",
planQuantity: 100,
workCenter: '装配线1',
status: 0
workCenter: "装配线1",
status: 0,
},
materialList: [
{
materialCode: 'MAT001',
materialName: '原材料A',
specification: '100*50*20mm',
locationCode: 'A01-01-01',
materialCode: "MAT001",
materialName: "原材料A",
specification: "100*50*20mm",
locationCode: "A01-01-01",
stock: 500,
requiredQuantity: 200,
issuedQuantity: 0,
currentPick: 0
currentPick: 0,
},
{
materialCode: 'MAT002',
materialName: '原材料B',
specification: '200*100*30mm',
locationCode: 'A01-02-01',
materialCode: "MAT002",
materialName: "原材料B",
specification: "200*100*30mm",
locationCode: "A01-02-01",
stock: 300,
requiredQuantity: 150,
issuedQuantity: 0,
currentPick: 0
}
currentPick: 0,
},
],
batchInfo: {
batchNo: '',
workOrder: ''
batchNo: "",
workOrder: "",
},
remark: '',
submitting: false
}
remark: "",
submitting: false,
};
},
mounted() {
this.loadOrderData()
this.loadOrderData();
},
methods: {
loadOrderData() {
const orderNo = this.$route.params.orderNo
console.log('加载生产订单数据:', orderNo)
const orderNo = this.$route.params.orderNo;
console.log("加载生产订单数据:", orderNo);
},
handleScanBatch() {
this.$toast('扫描批次功能开发中...')
this.$toast("扫描批次功能开发中...");
},
async handleSubmit() {
//
const hasPick = this.materialList.some(item => item.currentPick > 0)
const hasPick = this.materialList.some((item) => item.currentPick > 0);
if (!hasPick) {
this.$toast('请输入发料数量')
return
this.$toast("请输入发料数量");
return;
}
//
const insufficientStock = this.materialList.find(item => item.currentPick > item.stock)
const insufficientStock = this.materialList.find(
(item) => item.currentPick > item.stock
);
if (insufficientStock) {
this.$toast(`${insufficientStock.materialName} 库存不足`)
return
this.$toast(`${insufficientStock.materialName} 库存不足`);
return;
}
this.submitting = true
this.submitting = true;
try {
await new Promise(resolve => setTimeout(resolve, 2000))
await new Promise((resolve) => setTimeout(resolve, 2000));
this.$toast.success('发料成功')
this.$router.back()
this.$toast.success("发料成功");
this.$router.back();
} catch (error) {
this.$toast.fail('发料失败')
this.$toast.fail("发料失败");
} finally {
this.submitting = false
this.submitting = false;
}
},
getStatusText(status) {
const statusMap = {
0: '待发料',
1: '部分发料',
2: '已完成'
}
return statusMap[status] || '未知'
}
}
}
0: "待发料",
1: "部分发料",
2: "已完成",
};
return statusMap[status] || "未知";
},
},
};
</script>
<style scoped>

412
src/views/modules/production-return/productionReturnPicking.vue

@ -11,216 +11,234 @@
</div>
</div>
<!-- 搜索框 -->
<!-- 工单号输入 -->
<div class="search-container">
<el-input clearable
v-model="searchCode"
placeholder="请扫描工单号或退料申请单号"
prefix-icon="el-icon-search"
@keyup.enter.native="handleSearch"
ref="searchInput"
<el-input
clearable
v-model="workOrderNo"
placeholder="请输入工单号"
prefix-icon="el-icon-search"
@keyup.enter.native="handleSearchWorkOrder"
ref="workOrderInput"
/>
</div>
<!-- 退料单列表 -->
<div class="content-area">
<!-- 工单信息卡片列表 -->
<div class="work-order-list" v-if="workOrderList.length > 0">
<div
v-for="(item, index) in returnList"
v-for="(workOrder, index) in workOrderList"
:key="index"
class="return-card"
@click="goToReturnPage(item)"
class="work-order-card"
@click="selectWorkOrder(workOrder)"
>
<div class="card-title">
<span class="title-label">{{ item.type === 'workOrder' ? '工单号' : '退料申请单号' }}</span>
<span class="title-value">{{ item.orderNo }}</span>
<span class="title-label">工单号{{ workOrder.orderNo }}</span>
<span class="title-value">{{ workOrder.partNo }}</span>
</div>
<!-- 物料描述单独一行 -->
<div class="part-desc-row">
<span class="desc-text">{{ workOrder.partDesc }}</span>
</div>
<div class="card-details">
<div class="detail-item">
<div class="detail-label">{{ item.type === 'workOrder' ? '产品编码' : '关联工单' }}</div>
<div class="detail-value">{{ item.relatedNo }}</div>
<div class="detail-label">计划数量</div>
<div class="detail-value">{{ workOrder.qtyComplete }}</div>
</div>
<div class="detail-item">
<div class="detail-label">退料物料</div>
<div class="detail-value">
<span class="qualified">{{ item.returnedMaterials }}</span><span class="total">{{ item.totalMaterials }}</span>
</div>
<div class="detail-label">状态</div>
<div class="detail-value">{{ workOrder.status || "进行中" }}</div>
</div>
<div class="detail-item">
<div class="detail-label">退料数量</div>
<div class="detail-value">
<span class="qualified">{{ item.returnedQty }}</span><span class="total">{{ item.totalQty }}</span>
</div>
<div class="detail-label">单位</div>
<div class="detail-value">{{ workOrder.uom || "个" }}</div>
</div>
</div>
</div>
</div>
<!-- 空状态 -->
<div v-if="returnList.length === 0 && !loading" class="empty-state">
<i class="el-icon-box"></i>
<p>暂无待退料订单</p>
</div>
<!-- 材料列表 -->
<div
class="content-area"
v-if="selectedWorkOrder && materialList.length > 0"
>
<div
v-for="(material, index) in materialList"
:key="index"
class="material-card"
@click="selectMaterial(material)"
>
<div class="card-title">
<span class="title-label"
>物料编码{{ material.componentPartNo }} &nbsp;&nbsp; 行号{{
material.lineNo || index + 1
}}</span
>
</div>
<!-- 物料描述单独一行 -->
<div class="part-desc-row">
<span class="desc-text">{{ material.componentPartDesc }}</span>
</div>
<!-- 加载状态 -->
<div v-if="loading" class="loading-state">
<i class="el-icon-loading"></i>
<p>加载中...</p>
<div class="card-details">
<div class="detail-item">
<div class="detail-label">需求数量</div>
<div class="detail-value">{{ material.qtyRequired }}</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.uom || "个" }}</div>
</div>
</div>
</div>
</div>
<!-- 空状态 -->
<div
v-if="selectedWorkOrder && materialList.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>
</template>
<script>
import { getWorkOrderMaterials, getReturnRequestMaterials } from '@/api/production/production-return'
import {
getWorkOrderInfo,
getWorkOrderMaterials,
} from '@/api/production/production-issue';
import moment from 'moment';
export default {
data() {
return {
searchCode: '',
returnList: [],
loading: false
workOrderNo: "",
workOrderList: [],
selectedWorkOrder: null,
materialList: [],
selectedMaterial: null,
loading: false,
};
},
methods: {
formatDate(date) {
return date ? moment(date).format('YYYY-MM-DD') : '';
},
//
handleSearch() {
if (this.searchCode.trim()) {
this.searchReturnList(this.searchCode.trim());
} else {
this.loadReturnList();
//
handleSearchWorkOrder() {
if (!this.workOrderNo.trim()) {
this.$message.warning("请输入工单号");
return;
}
},
// 退
loadReturnList() {
this.loading = true;
const params = {
workOrderNo: this.workOrderNo.trim(),
site: this.$store.state.user.site,
status: '待退料',
}
console.log('params', params);
// 退
this.simulateReturnList(params).then((data) => {
this.loading = false;
if (data && data.code === 0) {
this.returnList = data.list || [];
} else {
this.$message.error(data.msg || '获取数据失败');
}
}).catch(() => {
this.loading = false;
this.$message.error('获取数据失败');
});
};
getWorkOrderInfo(params)
.then(({ data }) => {
this.loading = false;
console.log("工单信息", data);
if (
data.workOrders &&
data.workOrders.length > 0 &&
data.code === 0
) {
this.workOrderList = data.workOrders;
this.selectedWorkOrder = null;
this.materialList = [];
} else {
this.$message.error("未找到该工单信息");
this.workOrderList = [];
this.selectedWorkOrder = null;
this.materialList = [];
}
})
.catch((error) => {
this.loading = false;
console.error("查询工单信息失败:", error);
this.$message.error("查询工单信息失败");
});
},
// 退
searchReturnList(searchCode) {
this.loading = true;
//
selectWorkOrder(workOrder) {
this.selectedWorkOrder = workOrder;
this.loadMaterialList();
},
//
loadMaterialList() {
if (!this.selectedWorkOrder) {
this.materialList = [];
return;
}
const params = {
orderNo: searchCode,
workOrderNo: this.selectedWorkOrder.orderNo,
site: this.$store.state.user.site,
status: '待退料'
};
this.simulateReturnList(params).then((data) => {
this.loading = false;
if (data && data.code === 0) {
if (data.list.length === 0) {
this.$message.warning('未找到匹配的退料订单');
getWorkOrderMaterials(params)
.then(({ data }) => {
console.log("材料清单", data);
if (data && data.code === 0) {
this.materialList = (data.materials || []).map((item, index) => ({
...item,
id: index + 1,
}));
} else {
this.$message.error(data.msg || "获取材料清单失败");
this.materialList = [];
}
this.returnList = data.list || [];
} else {
this.$message.error(data.msg || '查询失败');
}
}).catch(() => {
this.loading = false;
this.$message.error('查询失败');
});
})
.catch((error) => {
console.error("获取材料清单失败:", error);
this.$message.error("获取材料清单失败");
});
},
// 退API
simulateReturnList(params) {
return new Promise((resolve) => {
setTimeout(() => {
const mockData = [
{
orderNo: 'WO202401001',
type: 'workOrder',
relatedNo: 'PROD001',
returnedMaterials: 2,
totalMaterials: 5,
returnedQty: 150,
totalQty: 500,
status: '待退料'
},
{
orderNo: 'RT202401001',
type: 'returnRequest',
relatedNo: 'WO202401002',
returnedMaterials: 0,
totalMaterials: 3,
returnedQty: 0,
totalQty: 200,
status: '待退料'
},
{
orderNo: 'WO202401003',
type: 'workOrder',
relatedNo: 'PROD003',
returnedMaterials: 1,
totalMaterials: 4,
returnedQty: 80,
totalQty: 300,
status: '待退料'
}
];
//
let filteredData = mockData;
if (params.orderNo) {
filteredData = mockData.filter(item =>
item.orderNo.includes(params.orderNo) ||
item.relatedNo.includes(params.orderNo)
);
}
resolve({
code: 0,
list: filteredData,
msg: '成功'
});
}, 500);
});
},
//
selectMaterial(material) {
console.log("选择材料", material);
// 退
goToReturnPage(item) {
this.$router.push({
name: 'productionReturnPickingDetail',
name: "productionReturnPickingDetail",
params: {
orderNo: item.orderNo,
orderType: item.type
}
orderNo: this.selectedWorkOrder.orderNo,
orderType:'workOrder'
},
});
}
},
},
mounted() {
//
//
this.$nextTick(() => {
if (this.$refs.searchInput) {
this.$refs.searchInput.focus();
if (this.$refs.workOrderInput) {
this.$refs.workOrderInput.focus();
}
});
//
this.loadReturnList();
}
},
};
</script>
@ -268,6 +286,45 @@ export default {
.search-container {
padding: 12px 16px;
background: white;
display: flex;
align-items: center;
gap: 12px;
}
.search-container .el-input {
flex: 1;
}
/* 工单列表 */
.work-order-list {
overflow-y: auto;
padding: 12px 16px;
}
/* 工单卡片 */
.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-color: #17b3a3;
background: #f0fffe;
}
.work-order-card:active {
transform: translateY(0);
}
/* 内容区域 */
@ -277,8 +334,8 @@ export default {
padding: 12px 16px;
}
/* 退料卡片 */
.return-card {
/* 料卡片 */
.material-card {
background: white;
border-radius: 8px;
margin-bottom: 12px;
@ -286,15 +343,20 @@ export default {
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
cursor: pointer;
transition: all 0.2s ease;
border-left: 4px solid #17B3A3;
border: 2px solid transparent;
}
.return-card:hover {
.material-card:hover {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
transform: translateY(-1px);
}
.return-card:active {
.material-card.selected {
border-color: #17b3a3;
background: #f0fffe;
}
.material-card:active {
transform: translateY(0);
}
@ -314,7 +376,20 @@ export default {
font-size: 16px;
font-weight: bold;
color: #333;
margin-left: 20px;
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;
}
/* 卡片详情 */
@ -328,8 +403,8 @@ export default {
.detail-item {
flex: 1;
text-align: center;
min-width: 60px;
max-width: 60px;
min-width: 50px;
max-width: 70px;
}
.detail-label {
@ -347,21 +422,6 @@ export default {
margin-left: -12px;
}
.detail-value .qualified {
color: #17B3A3;
font-weight: 500;
}
.detail-value .total {
color: #333;
font-weight: 500;
}
.detail-value .total::before {
content: '/';
color: #333;
}
/* 空状态 */
.empty-state {
display: flex;
@ -418,11 +478,19 @@ export default {
padding: 8px 12px;
}
.work-order-list {
padding: 8px 12px;
}
.work-order-card {
padding: 12px;
}
.content-area {
padding: 8px 12px;
}
.return-card {
.material-card {
padding: 12px;
}

Loading…
Cancel
Save