7 changed files with 650 additions and 261 deletions
-
2src/router/index.js
-
350src/views/modules/outsourcing-issue/DirectIssue.vue
-
50src/views/modules/outsourcing-issue/MoveIssue.vue
-
70src/views/modules/outsourcing-issue/PickingIssue.vue
-
58src/views/modules/outsourcing-issue/ReturnIssue.vue
-
345src/views/modules/outsourcing-issue/index.vue
-
36src/views/modules/production-issue/productionIssuePda.vue
@ -0,0 +1,350 @@ |
|||
<template> |
|||
<div class="input-section"> |
|||
<!-- PO号输入 --> |
|||
<div class="input-group" v-if="!materialList.length"> |
|||
<div class="input-group"> |
|||
<label>PO号</label> |
|||
<div class="input-with-scan"> |
|||
<input v-model="poNo" placeholder="请输入或扫描PO号" @keyup.enter="loadMaterials" /> |
|||
</div> |
|||
</div> |
|||
<div class="input-group"> |
|||
<button @click="loadMaterials" class="scan-btn">确认</button> |
|||
</div> |
|||
</div> |
|||
<!-- 物料列表 --> |
|||
<div class="materials-section" v-if="materialList.length"> |
|||
<div class="section-header"> |
|||
<h3>PO物料 ({{ poNo }})</h3> |
|||
<button @click="resetPO" class="reset-btn">重新选择</button> |
|||
</div> |
|||
<div class="material-list"> |
|||
<div v-for="(material, index) in materialList" :key="index" class="material-item" |
|||
:class="{ selected: selectedMaterial && selectedMaterial.partNo === material.partNo }" |
|||
@click="selectMaterial(material)"> |
|||
<div class="material-info"> |
|||
<div class="part-no">{{ material.partNo }}</div> |
|||
<div class="part-desc">{{ material.desc }}</div> |
|||
<div class="qty-info"> |
|||
需求: {{ material.qty }} | 已发: {{ material.recvQty }} | 剩余: {{ material.thisRecvQty }} |
|||
</div> |
|||
</div> |
|||
<div class="material-status"> |
|||
<span v-if="material.thisRecvQty > 0" class="status-pending">待发料</span> |
|||
<span v-else class="status-complete">已完成</span> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- 扫描标签 --> |
|||
<div class="scan-section" v-if="selectedMaterial"> |
|||
<div class="section-header"> |
|||
<h3>扫描物料标签</h3> |
|||
</div> |
|||
<div class="input-group"> |
|||
<label>物料标签</label> |
|||
<div class="input-with-scan"> |
|||
<input v-model="scannedLabel" placeholder="请扫描物料标签" @keyup.enter="parseMaterialLabel" /> |
|||
<button @click="parseMaterialLabel" class="scan-btn">解析</button> |
|||
</div> |
|||
</div> |
|||
<!-- 标签信息 --> |
|||
<div class="label-info" v-if="labelInfo"> |
|||
<div class="info-row"><span class="label">物料编码:</span><span class="value">{{ labelInfo.partNo }}</span></div> |
|||
<div class="info-row"><span class="label">批次号:</span><span class="value">{{ labelInfo.batchNo }}</span></div> |
|||
<div class="info-row"><span class="label">可用数量:</span><span class="value">{{ labelInfo.availableQty }}</span> |
|||
</div> |
|||
</div> |
|||
<!-- 发料数量 --> |
|||
<div class="qty-input" v-if="labelInfo"> |
|||
<div class="input-group"> |
|||
<label>发料数量</label> |
|||
<input v-model="issueQty" type="number" :max="Math.min(selectedMaterial.thisRecvQty, labelInfo.availableQty)" |
|||
placeholder="请输入发料数量" /> |
|||
</div> |
|||
<div class="input-group"> |
|||
<label>备注</label> |
|||
<input v-model="remark" placeholder="可选" /> |
|||
</div> |
|||
<button @click="confirmIssue" class="confirm-btn" :disabled="!issueQty">确认发料</button> |
|||
</div> |
|||
</div> |
|||
<!-- 消息提示 --> |
|||
<div class="message" v-if="message" :class="messageType">{{ message }}</div> |
|||
</div> |
|||
</template> |
|||
<script> |
|||
import { getPoList } from '@/api/po/po.js' |
|||
export default { |
|||
name: 'DirectIssue', |
|||
data() { |
|||
return { |
|||
poNo: '', |
|||
materialList: [], |
|||
selectedMaterial: null, |
|||
scannedLabel: '', |
|||
labelInfo: null, |
|||
issueQty: null, |
|||
remark: '', |
|||
message: '', |
|||
messageType: 'info', |
|||
} |
|||
}, |
|||
methods: { |
|||
async loadMaterials() { |
|||
if (!this.poNo) { |
|||
this.showMessage('请输入PO号', 'error') |
|||
return |
|||
} |
|||
// 调用API获取PO物料 |
|||
try { |
|||
const { data } = await getPoList({ |
|||
poNumber: this.poNo, |
|||
site: localStorage.getItem('site'), |
|||
}) |
|||
if (data.code === 0 && data.rows && data.rows.length > 0) { |
|||
this.materialList = data.rows |
|||
} else { |
|||
this.showMessage(data.msg || '未找到PO物料', 'warning') |
|||
} |
|||
} catch (e) { |
|||
this.showMessage('网络错误', 'error') |
|||
} |
|||
}, |
|||
selectMaterial(material) { |
|||
if (material.thisRecvQty <= 0) { |
|||
this.showMessage('该物料已发料完成', 'warning') |
|||
return |
|||
} |
|||
this.selectedMaterial = material |
|||
this.scannedLabel = '' |
|||
this.labelInfo = null |
|||
this.issueQty = null |
|||
}, |
|||
resetPO() { |
|||
this.poNo = '' |
|||
this.materialList = [] |
|||
this.selectedMaterial = null |
|||
this.scannedLabel = '' |
|||
this.labelInfo = null |
|||
this.issueQty = null |
|||
}, |
|||
parseMaterialLabel() { |
|||
// TODO: 调用解析标签API,获取labelInfo |
|||
this.labelInfo = { |
|||
partNo: this.selectedMaterial.partNo, |
|||
batchNo: 'BATCH001', |
|||
availableQty: 100, |
|||
} |
|||
this.issueQty = null |
|||
this.showMessage('标签解析成功', 'success') |
|||
}, |
|||
confirmIssue() { |
|||
if (!this.issueQty || this.issueQty <= 0) { |
|||
this.showMessage('请输入有效的发料数量', 'error') |
|||
return |
|||
} |
|||
// TODO: 调用发料API |
|||
this.showMessage('发料成功', 'success') |
|||
// 刷新物料列表 |
|||
this.loadMaterials() |
|||
this.selectedMaterial = null |
|||
this.scannedLabel = '' |
|||
this.labelInfo = null |
|||
this.issueQty = null |
|||
}, |
|||
showMessage(text, type = 'info') { |
|||
this.message = text |
|||
this.messageType = type |
|||
setTimeout(() => { |
|||
this.message = '' |
|||
}, 2000) |
|||
}, |
|||
}, |
|||
} |
|||
</script> |
|||
<style scoped> |
|||
.input-section { |
|||
background: white; |
|||
border-radius: 8px; |
|||
padding: 15px; |
|||
margin-bottom: 15px; |
|||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
|||
} |
|||
.section-header { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
margin-bottom: 15px; |
|||
padding-bottom: 10px; |
|||
border-bottom: 1px solid #eee; |
|||
} |
|||
.reset-btn { |
|||
background: #17b3a3; |
|||
color: white; |
|||
border: none; |
|||
padding: 6px 12px; |
|||
border-radius: 4px; |
|||
cursor: pointer; |
|||
font-size: 12px; |
|||
} |
|||
.input-group { |
|||
margin-bottom: 15px; |
|||
} |
|||
.input-group label { |
|||
display: block; |
|||
margin-bottom: 5px; |
|||
font-weight: bold; |
|||
color: #333; |
|||
} |
|||
.input-with-scan { |
|||
display: flex; |
|||
gap: 10px; |
|||
} |
|||
.input-with-scan input { |
|||
flex: 1; |
|||
padding: 10px; |
|||
border: 1px solid #ddd; |
|||
border-radius: 4px; |
|||
font-size: 16px; |
|||
} |
|||
.scan-btn, |
|||
.confirm-btn { |
|||
background: #17b3a3; |
|||
color: white; |
|||
border: none; |
|||
padding: 10px 15px; |
|||
border-radius: 4px; |
|||
cursor: pointer; |
|||
font-size: 14px; |
|||
white-space: nowrap; |
|||
} |
|||
.scan-btn:hover, |
|||
.confirm-btn:hover { |
|||
background: #13998c; |
|||
} |
|||
.confirm-btn:disabled { |
|||
background: #6c757d; |
|||
cursor: not-allowed; |
|||
} |
|||
.material-list { |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 10px; |
|||
} |
|||
.material-item { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
padding: 15px; |
|||
border: 1px solid #ddd; |
|||
border-radius: 6px; |
|||
cursor: pointer; |
|||
transition: all 0.3s; |
|||
} |
|||
.material-item.selected { |
|||
border-color: #007bff; |
|||
background-color: #e3f2fd; |
|||
} |
|||
.material-info { |
|||
flex: 1; |
|||
} |
|||
.part-no { |
|||
font-weight: bold; |
|||
font-size: 16px; |
|||
color: #333; |
|||
margin-bottom: 4px; |
|||
} |
|||
.part-desc { |
|||
color: #666; |
|||
font-size: 14px; |
|||
margin-bottom: 4px; |
|||
} |
|||
.qty-info { |
|||
font-size: 12px; |
|||
color: #666; |
|||
} |
|||
.material-status { |
|||
text-align: right; |
|||
} |
|||
.status-pending { |
|||
background: #ffc107; |
|||
color: #212529; |
|||
padding: 4px 8px; |
|||
border-radius: 12px; |
|||
font-size: 12px; |
|||
} |
|||
.status-complete { |
|||
background: #28a745; |
|||
color: white; |
|||
padding: 4px 8px; |
|||
border-radius: 12px; |
|||
font-size: 12px; |
|||
} |
|||
.label-info { |
|||
background: #f8f9fa; |
|||
border-radius: 6px; |
|||
padding: 15px; |
|||
margin-bottom: 15px; |
|||
} |
|||
.info-row { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
margin-bottom: 8px; |
|||
padding: 5px 0; |
|||
border-bottom: 1px solid #eee; |
|||
} |
|||
.info-row:last-child { |
|||
border-bottom: none; |
|||
margin-bottom: 0; |
|||
} |
|||
.info-row .label { |
|||
font-weight: bold; |
|||
color: #666; |
|||
} |
|||
.info-row .value { |
|||
color: #333; |
|||
} |
|||
.qty-input { |
|||
margin-top: 15px; |
|||
} |
|||
.qty-input input[type='number'] { |
|||
width: 100%; |
|||
padding: 10px; |
|||
border: 1px solid #ddd; |
|||
border-radius: 4px; |
|||
font-size: 16px; |
|||
} |
|||
.message { |
|||
margin-top: 10px; |
|||
padding: 8px 12px; |
|||
border-radius: 4px; |
|||
font-weight: bold; |
|||
} |
|||
.message.success { |
|||
background: #d4edda; |
|||
color: #155724; |
|||
border: 1px solid #c3e6cb; |
|||
} |
|||
.message.error { |
|||
background: #f8d7da; |
|||
color: #721c24; |
|||
border: 1px solid #f5c6cb; |
|||
} |
|||
.message.warning { |
|||
background: #fff3cd; |
|||
color: #856404; |
|||
border: 1px solid #ffeaa7; |
|||
} |
|||
.message.info { |
|||
background: #d1ecf1; |
|||
color: #0c5460; |
|||
border: 1px solid #bee5eb; |
|||
} |
|||
.confirm-btn { |
|||
width: 100%; |
|||
} |
|||
.scan-btn { |
|||
width: 100%; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,50 @@ |
|||
<template> |
|||
<div class="input-section"> |
|||
<!-- 申请单/PO号输入 --> |
|||
<div class="input-group"> |
|||
<label>申请单/PO号</label> |
|||
<div class="input-with-scan"> |
|||
<input v-model="orderNo" placeholder="请输入申请单号或PO号" /> |
|||
<button class="scan-btn">确认</button> |
|||
</div> |
|||
</div> |
|||
<!-- 选择库位 --> |
|||
<div class="input-group"> |
|||
<label>目标库位</label> |
|||
<div class="input-with-scan"> |
|||
<input v-model="location" placeholder="请输入目标库位" /> |
|||
<button class="scan-btn">选择</button> |
|||
</div> |
|||
</div> |
|||
<!-- 生成移库记录 --> |
|||
<div class="input-group"> |
|||
<button class="confirm-btn">生成移库记录</button> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
<script> |
|||
export default { |
|||
name: 'MoveIssue', |
|||
data() { |
|||
return { |
|||
orderNo: '', |
|||
location: '' |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
<style scoped> |
|||
.input-section { background: white; border-radius: 8px; padding: 15px; margin-bottom: 15px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } |
|||
.section-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; padding-bottom: 10px; border-bottom: 1px solid #eee; } |
|||
.reset-btn { background: #17b3a3; color: white; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size: 12px; } |
|||
.input-group { margin-bottom: 15px; } |
|||
.input-group label { display: block; margin-bottom: 5px; font-weight: bold; color: #333; } |
|||
.input-with-scan { display: flex; gap: 10px; } |
|||
.input-with-scan input { flex: 1; padding: 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 16px; } |
|||
.scan-btn, .confirm-btn { background: #17b3a3; color: white; border: none; padding: 10px 15px; border-radius: 4px; cursor: pointer; font-size: 14px; white-space: nowrap; } |
|||
.scan-btn:hover, .confirm-btn:hover { background: #13998c; } |
|||
.confirm-btn:disabled { background: #6c757d; cursor: not-allowed; } |
|||
.confirm-btn { |
|||
width: 100%; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,70 @@ |
|||
<template> |
|||
<div class="input-section"> |
|||
<!-- 申请单号输入 --> |
|||
<div class="input-group"> |
|||
<label>申请单号</label> |
|||
<div class="input-with-scan"> |
|||
<input v-model="notifyNo" placeholder="请输入申请单号" /> |
|||
<button class="scan-btn">创建托盘</button> |
|||
</div> |
|||
</div> |
|||
<!-- 扫描箱卷绑定 --> |
|||
<div class="input-group"> |
|||
<label>扫描箱/卷</label> |
|||
<div class="input-with-scan"> |
|||
<input v-model="scannedUnit" placeholder="请扫描处理单元条码" /> |
|||
<button class="scan-btn">绑定</button> |
|||
</div> |
|||
</div> |
|||
<!-- 已绑定单元列表 --> |
|||
<div class="bound-units"> |
|||
<div class="section-header"> |
|||
<h4>已绑定单元 (0)</h4> |
|||
</div> |
|||
<div class="unit-list"> |
|||
<!-- TODO: 列表渲染绑定单元 --> |
|||
</div> |
|||
</div> |
|||
<!-- 打印托盘标签 --> |
|||
<div class="print-section"> |
|||
<div class="input-group"> |
|||
<label>打印机</label> |
|||
<select> |
|||
<option value="">请选择打印机</option> |
|||
<option value="PRINTER_01">打印机01</option> |
|||
<option value="PRINTER_02">打印机02</option> |
|||
</select> |
|||
</div> |
|||
<button class="print-btn">打印托盘标签</button> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
<script> |
|||
export default { |
|||
name: 'PickingIssue', |
|||
data() { |
|||
return { |
|||
notifyNo: '', |
|||
scannedUnit: '' |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
<style scoped> |
|||
.input-section { background: white; border-radius: 8px; padding: 15px; margin-bottom: 15px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } |
|||
.section-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; padding-bottom: 10px; border-bottom: 1px solid #eee; } |
|||
.reset-btn { background: #17b3a3; color: white; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size: 12px; } |
|||
.input-group { margin-bottom: 15px; } |
|||
.input-group label { display: block; margin-bottom: 5px; font-weight: bold; color: #333; } |
|||
.input-with-scan { display: flex; gap: 10px; } |
|||
.input-with-scan input { flex: 1; padding: 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 16px; } |
|||
.scan-btn, .print-btn { background: #17b3a3; color: white; border: none; padding: 10px 15px; border-radius: 4px; cursor: pointer; font-size: 14px; white-space: nowrap; } |
|||
.scan-btn:hover, .print-btn:hover { background: #13998c; } |
|||
.print-btn:disabled { background: #6c757d; cursor: not-allowed; } |
|||
.bound-units { margin-top: 20px; } |
|||
.unit-list { display: flex; flex-direction: column; gap: 8px; max-height: 200px; overflow-y: auto; } |
|||
.unit-item { background: #e9ecef; padding: 10px; border-radius: 4px; font-family: monospace; font-size: 14px; } |
|||
.print-btn { |
|||
width: 100%; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,58 @@ |
|||
<template> |
|||
<div class="input-section"> |
|||
<!-- 退料类型选择 --> |
|||
<div class="input-group"> |
|||
<label>退料类型</label> |
|||
<select v-model="returnType"> |
|||
<option value="">请选择退料类型</option> |
|||
<option value="over">多发退料</option> |
|||
<option value="quality">质量退料</option> |
|||
<option value="other">其他退料</option> |
|||
</select> |
|||
</div> |
|||
<!-- 退料数量 --> |
|||
<div class="input-group"> |
|||
<label>退料数量</label> |
|||
<input v-model="returnQty" type="number" placeholder="请输入退料数量" /> |
|||
<!-- 生成退料记录 --> |
|||
|
|||
</div> |
|||
<button class="scan-btn">生成退料记录</button> |
|||
|
|||
</div> |
|||
</template> |
|||
<script> |
|||
export default { |
|||
name: 'ReturnIssue', |
|||
data() { |
|||
return { |
|||
returnType: '', |
|||
returnQty: '' |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
<style scoped> |
|||
.input-section { background: white; border-radius: 8px; padding: 15px; margin-bottom: 15px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } |
|||
.section-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; padding-bottom: 10px; border-bottom: 1px solid #eee; } |
|||
.reset-btn { background: #17b3a3; color: white; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size: 12px; } |
|||
.input-group { margin-bottom: 15px; } |
|||
.input-group label { display: block; margin-bottom: 5px; font-weight: bold; color: #333; } |
|||
.input-group select, .input-group input { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 16px; } |
|||
.scan-btn, .confirm-btn, .print-btn { |
|||
background: #17b3a3; |
|||
color: white; |
|||
border: none; |
|||
padding: 10px 15px; |
|||
border-radius: 4px; |
|||
cursor: pointer; |
|||
font-size: 14px; |
|||
white-space: nowrap; |
|||
width: 100%; |
|||
} |
|||
|
|||
.scan-btn:hover, .confirm-btn:hover, .print-btn:hover { |
|||
background: #13998c; |
|||
} |
|||
.confirm-btn:disabled { background: #6c757d; cursor: not-allowed; } |
|||
</style> |
|||
@ -1,280 +1,141 @@ |
|||
<template> |
|||
<div class="outsourcing-issue-container"> |
|||
<van-nav-bar title="委外发料" left-arrow @click-left="$router.back()" /> |
|||
|
|||
<!-- 搜索栏 --> |
|||
<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="pda-container"> |
|||
<div class="status-bar"> |
|||
<div class="goBack" @click="goBack"><i class="el-icon-arrow-left"></i>上一页</div> |
|||
<div class="goBack">{{ functionTitle }}</div> |
|||
<div class="network" style="color: #fff" @click="$router.push({ path: '/' })">🏠首页</div> |
|||
</div> |
|||
<div style="overflow-y: auto"> |
|||
<!-- 功能选择 --> |
|||
<div class="function-selector" v-if="!selectedFunction"> |
|||
<div class="function-card" @click="selectFunction('direct')"> |
|||
<div class="function-icon">📦</div> |
|||
<div class="function-title">直接发料</div> |
|||
<div class="function-desc">输入工单/PO号,扫描物料标签直接发料</div> |
|||
</div> |
|||
<div class="order-info"> |
|||
<div class="info-row"> |
|||
<span class="label">供应商:</span> |
|||
<span class="value">{{ item.supplier }}</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.outsourcingQuantity }}</span> |
|||
</div> |
|||
<div class="info-row"> |
|||
<span class="label">交货日期:</span> |
|||
<span class="value">{{ item.deliveryDate }}</span> |
|||
</div> |
|||
<div class="info-row"> |
|||
<span class="label">联系人:</span> |
|||
<span class="value">{{ item.contactPerson }}</span> |
|||
</div> |
|||
<div class="function-card" @click="selectFunction('picking')"> |
|||
<div class="function-icon">🏗️</div> |
|||
<div class="function-title">拣选装托盘</div> |
|||
<div class="function-desc">基于申请单创建托盘,扫描箱卷绑定</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 class="function-card" @click="selectFunction('move')"> |
|||
<div class="function-icon">🚚</div> |
|||
<div class="function-title">移库发料</div> |
|||
<div class="function-desc">输入申请单/PO号,选择库位,生成移库记录</div> |
|||
</div> |
|||
<div class="function-card" @click="selectFunction('return')"> |
|||
<div class="function-icon">↩️</div> |
|||
<div class="function-title">退料</div> |
|||
<div class="function-desc">选择退料类型,录入退料数量,生成退料记录</div> |
|||
</div> |
|||
</div> |
|||
<!-- 直接发料骨架 --> |
|||
<div v-if="selectedFunction === 'direct'"> |
|||
<direct-issue @back="resetAll" /> |
|||
</div> |
|||
<!-- 拣选装托盘骨架 --> |
|||
<div v-if="selectedFunction === 'picking'"> |
|||
<picking-issue @back="resetAll" /> |
|||
</div> |
|||
</van-list> |
|||
</van-pull-refresh> |
|||
<!-- 移库发料骨架 --> |
|||
<div v-if="selectedFunction === 'move'"> |
|||
<move-issue @back="resetAll" /> |
|||
</div> |
|||
<!-- 退料骨架 --> |
|||
<div v-if="selectedFunction === 'return'"> |
|||
<return-issue @back="resetAll" /> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import DirectIssue from './DirectIssue.vue' |
|||
import PickingIssue from './PickingIssue.vue' |
|||
import MoveIssue from './MoveIssue.vue' |
|||
import ReturnIssue from './ReturnIssue.vue' |
|||
|
|||
export default { |
|||
name: 'OutsourcingIssue', |
|||
name: 'OutsourcingIssuePDA', |
|||
components: { |
|||
DirectIssue, |
|||
PickingIssue, |
|||
MoveIssue, |
|||
ReturnIssue |
|||
}, |
|||
data() { |
|||
return { |
|||
searchValue: '', |
|||
activeTab: 'all', |
|||
refreshing: false, |
|||
loading: false, |
|||
finished: false, |
|||
orderList: [ |
|||
{ |
|||
id: 1, |
|||
orderNo: 'OS202401001', |
|||
supplier: '委外供应商A', |
|||
productName: '委外产品A', |
|||
outsourcingQuantity: 500, |
|||
deliveryDate: '2024-01-25', |
|||
contactPerson: '张经理', |
|||
totalMaterialQuantity: 1000, |
|||
issuedQuantity: 0, |
|||
status: 0 |
|||
}, |
|||
{ |
|||
id: 2, |
|||
orderNo: 'OS202401002', |
|||
supplier: '委外供应商B', |
|||
productName: '委外产品B', |
|||
outsourcingQuantity: 300, |
|||
deliveryDate: '2024-01-22', |
|||
contactPerson: '李经理', |
|||
totalMaterialQuantity: 600, |
|||
issuedQuantity: 400, |
|||
status: 1 |
|||
}, |
|||
{ |
|||
id: 3, |
|||
orderNo: 'OS202401003', |
|||
supplier: '委外供应商C', |
|||
productName: '委外产品C', |
|||
outsourcingQuantity: 200, |
|||
deliveryDate: '2024-01-18', |
|||
contactPerson: '王经理', |
|||
totalMaterialQuantity: 400, |
|||
issuedQuantity: 400, |
|||
status: 2 |
|||
} |
|||
] |
|||
selectedFunction: null |
|||
} |
|||
}, |
|||
mounted() { |
|||
this.loadData() |
|||
computed: { |
|||
functionTitle() { |
|||
if (!this.selectedFunction) return '委外发料'; |
|||
if (this.selectedFunction === 'direct') return '直接发料'; |
|||
if (this.selectedFunction === 'picking') return '拣选装托盘'; |
|||
if (this.selectedFunction === 'move') return '移库发料'; |
|||
if (this.selectedFunction === 'return') return '退料'; |
|||
return '委外发料'; |
|||
} |
|||
}, |
|||
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) |
|||
selectFunction(func) { |
|||
this.selectedFunction = func |
|||
}, |
|||
onLoad() { |
|||
this.loadData() |
|||
resetAll() { |
|||
this.selectedFunction = null |
|||
}, |
|||
handleSearch() { |
|||
this.loadData() |
|||
}, |
|||
handleClear() { |
|||
this.searchValue = '' |
|||
this.loadData() |
|||
}, |
|||
handleTabChange() { |
|||
this.loadData() |
|||
}, |
|||
handleOrderClick(item) { |
|||
if (item.status === 0 || item.status === 1) { |
|||
this.$router.push(`/outsourcing-issue/pick/${item.orderNo}`) |
|||
goBack() { |
|||
if (!this.selectedFunction) { |
|||
this.$router.push('/') |
|||
} 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' |
|||
this.selectedFunction = null |
|||
} |
|||
return classMap[status] || '' |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.outsourcing-issue-container { |
|||
.outsourcing-issue { |
|||
padding: 10px; |
|||
font-family: Arial, sans-serif; |
|||
background-color: #f5f5f5; |
|||
min-height: 100vh; |
|||
background-color: #f7f8fa; |
|||
} |
|||
|
|||
.search-section { |
|||
padding: 10px 16px; |
|||
background: white; |
|||
} |
|||
|
|||
.filter-section { |
|||
background: white; |
|||
border-bottom: 1px solid #ebedf0; |
|||
.function-selector { |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 15px; |
|||
padding: 20px 0; |
|||
} |
|||
|
|||
.order-item { |
|||
.function-card { |
|||
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; |
|||
padding: 20px; |
|||
box-shadow: 0 2px 4px rgba(0,0,0,0.1); |
|||
cursor: pointer; |
|||
transition: all 0.3s; |
|||
text-align: center; |
|||
} |
|||
|
|||
.status-pending { |
|||
background-color: #ff976a; |
|||
.function-card:hover { |
|||
transform: translateY(-2px); |
|||
box-shadow: 0 4px 8px rgba(0,0,0,0.15); |
|||
} |
|||
|
|||
.status-partial { |
|||
background-color: #1989fa; |
|||
.function-icon { |
|||
font-size: 48px; |
|||
margin-bottom: 10px; |
|||
} |
|||
|
|||
.status-completed { |
|||
background-color: #07c160; |
|||
.function-title { |
|||
font-size: 18px; |
|||
font-weight: bold; |
|||
margin-bottom: 8px; |
|||
color: #333; |
|||
} |
|||
|
|||
.order-info { |
|||
.function-desc { |
|||
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; |
|||
color: #666; |
|||
} |
|||
</style> |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue