12 changed files with 1935 additions and 988 deletions
-
22src/api/production/production-inbound.js
-
9src/api/sales-return/sales-return.js
-
3src/i18n/locales/cn.js
-
3src/i18n/locales/en.js
-
8src/router/index.js
-
275src/views/common/login.vue
-
56src/views/main.vue
-
1434src/views/modules/production-inbound/index.vue
-
394src/views/modules/production-inbound/receive.vue
-
291src/views/modules/sales-return/index.vue
-
241src/views/modules/sales-return/sales-return-inbound.vue
-
133src/views/modules/sales-return/sales-return-scrap.vue
@ -0,0 +1,22 @@ |
|||||
|
import { createAPI } from "@/utils/httpRequest.js"; |
||||
|
|
||||
|
// 查询处理单元列表
|
||||
|
export const getOrderInfo = data => createAPI(`/production/inbound/getOrderInfo`,'post',data) |
||||
|
// 创建包装箱
|
||||
|
export const createPackage = data => createAPI(`/production/inbound/createPackage`,'post',data) |
||||
|
// 打印箱标签
|
||||
|
export const printPackageLabel = data => createAPI(`/production/inbound/printPackageLabel`,'post',data) |
||||
|
// 装托盘
|
||||
|
export const packToPallet = data => createAPI(`/production/inbound/packToPallet`,'post',data) |
||||
|
// 扫描包装箱获取信息
|
||||
|
export const scanPackage = data => createAPI(`/production/inbound/scanPackage`,'post',data) |
||||
|
// 根据包装编码获取包装信息
|
||||
|
export const getPackageInfo = data => createAPI(`/production/inbound/getPackageInfo`,'post',data) |
||||
|
// 入库登记
|
||||
|
export const inboundRegister = data => createAPI(`/production/inbound/inboundRegister`,'post',data) |
||||
|
// 查询入库记录
|
||||
|
export const getInboundRecords = data => createAPI(`/production/inbound/getInboundRecords`,'post',data) |
||||
|
// 生产订单退库
|
||||
|
export const returnStock = data => createAPI(`/production/inbound/returnStock`,'post',data) |
||||
|
// 扫描单元获取退库信息
|
||||
|
export const scanUnitForReturn = data => createAPI(`/production/inbound/scanUnitForReturn`,'post',data) |
||||
@ -0,0 +1,9 @@ |
|||||
|
import { createAPI } from "@/utils/httpRequest.js"; |
||||
|
|
||||
|
|
||||
|
// 获取采购单信息
|
||||
|
export const processReturn = data => createAPI(`salesreturn/processReturn`,'post',data) |
||||
|
|
||||
|
// 接收采购单信息
|
||||
|
export const getRmaList = data => createAPI(`salesreturn/getRmaList`,'post',data) |
||||
|
|
||||
1434
src/views/modules/production-inbound/index.vue
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -1,394 +0,0 @@ |
|||||
<template> |
|
||||
<div class="receive-container"> |
|
||||
<van-nav-bar title="生产入库" left-arrow @click-left="$router.back()" /> |
|
||||
|
|
||||
<!-- 订单信息 --> |
|
||||
<div class="order-info"> |
|
||||
<div class="info-header"> |
|
||||
<div class="order-no">{{ orderInfo.orderNo }}</div> |
|
||||
<div class="order-status">{{ getStatusText(orderInfo.status) }}</div> |
|
||||
</div> |
|
||||
<van-cell-group> |
|
||||
<van-cell title="产品编码" :value="orderInfo.productCode" /> |
|
||||
<van-cell title="产品名称" :value="orderInfo.productName" /> |
|
||||
<van-cell title="计划数量" :value="orderInfo.planQuantity" /> |
|
||||
<van-cell title="已完工" :value="orderInfo.completedQuantity" /> |
|
||||
<van-cell title="已入库" :value="orderInfo.inboundQuantity" /> |
|
||||
</van-cell-group> |
|
||||
</div> |
|
||||
|
|
||||
<!-- 入库明细 --> |
|
||||
<div class="inbound-section"> |
|
||||
<div class="section-title">入库明细</div> |
|
||||
<div class="inbound-item"> |
|
||||
<div class="product-info"> |
|
||||
<div class="product-name">{{ orderInfo.productCode }} - {{ orderInfo.productName }}</div> |
|
||||
<div class="product-spec">规格:{{ orderInfo.specification }}</div> |
|
||||
<div class="product-quantity"> |
|
||||
可入库:{{ orderInfo.completedQuantity - orderInfo.inboundQuantity }} |
|
||||
</div> |
|
||||
</div> |
|
||||
<div class="inbound-input"> |
|
||||
<van-stepper |
|
||||
v-model="inboundQuantity" |
|
||||
:min="0" |
|
||||
:max="orderInfo.completedQuantity - orderInfo.inboundQuantity" |
|
||||
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.qualityStatus" |
|
||||
label="质量状态" |
|
||||
placeholder="请选择质量状态" |
|
||||
readonly |
|
||||
is-link |
|
||||
@click="showQualityPicker = true" |
|
||||
/> |
|
||||
<van-field |
|
||||
v-model="qualityInfo.qualityRemark" |
|
||||
label="检验备注" |
|
||||
type="textarea" |
|
||||
placeholder="请输入检验备注" |
|
||||
rows="2" |
|
||||
/> |
|
||||
</div> |
|
||||
|
|
||||
<!-- 批次信息 --> |
|
||||
<div class="batch-section"> |
|
||||
<div class="section-title">批次信息</div> |
|
||||
<van-field |
|
||||
v-model="batchInfo.batchNo" |
|
||||
label="批次号" |
|
||||
placeholder="系统自动生成" |
|
||||
disabled |
|
||||
/> |
|
||||
<van-field |
|
||||
v-model="batchInfo.productionDate" |
|
||||
label="生产日期" |
|
||||
placeholder="请选择生产日期" |
|
||||
readonly |
|
||||
is-link |
|
||||
@click="showDatePicker = true" |
|
||||
/> |
|
||||
<van-field |
|
||||
v-model="batchInfo.expiryDate" |
|
||||
label="有效期至" |
|
||||
placeholder="请选择有效期" |
|
||||
readonly |
|
||||
is-link |
|
||||
@click="showExpiryPicker = true" |
|
||||
/> |
|
||||
</div> |
|
||||
|
|
||||
<!-- 库位分配 --> |
|
||||
<div class="location-section"> |
|
||||
<div class="section-title">库位分配</div> |
|
||||
<van-field |
|
||||
v-model="selectedLocation" |
|
||||
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="showDatePicker" position="bottom"> |
|
||||
<van-datetime-picker |
|
||||
v-model="productionDate" |
|
||||
type="date" |
|
||||
title="选择生产日期" |
|
||||
@confirm="onDateConfirm" |
|
||||
@cancel="showDatePicker = false" |
|
||||
/> |
|
||||
</van-popup> |
|
||||
|
|
||||
<van-popup v-model="showExpiryPicker" position="bottom"> |
|
||||
<van-datetime-picker |
|
||||
v-model="expiryDate" |
|
||||
type="date" |
|
||||
title="选择有效期" |
|
||||
@confirm="onExpiryConfirm" |
|
||||
@cancel="showExpiryPicker = 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: 'ProductionReceive', |
|
||||
data() { |
|
||||
return { |
|
||||
orderInfo: { |
|
||||
orderNo: 'MO202401001', |
|
||||
productCode: 'PROD001', |
|
||||
productName: '成品A', |
|
||||
specification: '标准规格', |
|
||||
planQuantity: 100, |
|
||||
completedQuantity: 80, |
|
||||
inboundQuantity: 0, |
|
||||
status: 0 |
|
||||
}, |
|
||||
inboundQuantity: 0, |
|
||||
qualityInfo: { |
|
||||
inspector: '', |
|
||||
qualityStatus: '', |
|
||||
qualityRemark: '' |
|
||||
}, |
|
||||
batchInfo: { |
|
||||
batchNo: 'BATCH' + Date.now(), |
|
||||
productionDate: '', |
|
||||
expiryDate: '' |
|
||||
}, |
|
||||
selectedLocation: '', |
|
||||
remark: '', |
|
||||
submitting: false, |
|
||||
showQualityPicker: false, |
|
||||
showDatePicker: false, |
|
||||
showExpiryPicker: false, |
|
||||
showLocationPicker: false, |
|
||||
productionDate: new Date(), |
|
||||
expiryDate: new Date(), |
|
||||
qualityColumns: [ |
|
||||
'合格', |
|
||||
'待检', |
|
||||
'不合格', |
|
||||
'返工' |
|
||||
], |
|
||||
locationColumns: [ |
|
||||
'FG01-01-01', |
|
||||
'FG01-01-02', |
|
||||
'FG01-02-01', |
|
||||
'FG01-02-02', |
|
||||
'FG02-01-01' |
|
||||
] |
|
||||
} |
|
||||
}, |
|
||||
mounted() { |
|
||||
this.loadOrderData() |
|
||||
}, |
|
||||
methods: { |
|
||||
loadOrderData() { |
|
||||
const orderNo = this.$route.params.orderNo |
|
||||
console.log('加载生产订单数据:', orderNo) |
|
||||
}, |
|
||||
handleScanLocation() { |
|
||||
this.$toast('扫描库位功能开发中...') |
|
||||
}, |
|
||||
onQualityConfirm(value) { |
|
||||
this.qualityInfo.qualityStatus = value |
|
||||
this.showQualityPicker = false |
|
||||
}, |
|
||||
onDateConfirm(value) { |
|
||||
this.batchInfo.productionDate = this.formatDate(value) |
|
||||
this.showDatePicker = false |
|
||||
}, |
|
||||
onExpiryConfirm(value) { |
|
||||
this.batchInfo.expiryDate = this.formatDate(value) |
|
||||
this.showExpiryPicker = false |
|
||||
}, |
|
||||
onLocationConfirm(value) { |
|
||||
this.selectedLocation = value |
|
||||
this.showLocationPicker = false |
|
||||
}, |
|
||||
formatDate(date) { |
|
||||
const year = date.getFullYear() |
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0') |
|
||||
const day = String(date.getDate()).padStart(2, '0') |
|
||||
return `${year}-${month}-${day}` |
|
||||
}, |
|
||||
async handleSubmit() { |
|
||||
// 验证入库数量 |
|
||||
if (this.inboundQuantity <= 0) { |
|
||||
this.$toast('请输入入库数量') |
|
||||
return |
|
||||
} |
|
||||
|
|
||||
// 验证质量状态 |
|
||||
if (!this.qualityInfo.qualityStatus) { |
|
||||
this.$toast('请选择质量状态') |
|
||||
return |
|
||||
} |
|
||||
|
|
||||
// 验证库位 |
|
||||
if (!this.selectedLocation) { |
|
||||
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; |
|
||||
} |
|
||||
|
|
||||
.order-info { |
|
||||
background: white; |
|
||||
margin-bottom: 10px; |
|
||||
} |
|
||||
|
|
||||
.info-header { |
|
||||
display: flex; |
|
||||
justify-content: space-between; |
|
||||
align-items: center; |
|
||||
padding: 16px; |
|
||||
border-bottom: 1px solid #ebedf0; |
|
||||
} |
|
||||
|
|
||||
.order-no { |
|
||||
font-size: 18px; |
|
||||
font-weight: bold; |
|
||||
color: #323233; |
|
||||
} |
|
||||
|
|
||||
.order-status { |
|
||||
padding: 4px 8px; |
|
||||
border-radius: 4px; |
|
||||
font-size: 12px; |
|
||||
color: white; |
|
||||
background-color: #ff976a; |
|
||||
} |
|
||||
|
|
||||
.inbound-section, |
|
||||
.quality-section, |
|
||||
.batch-section, |
|
||||
.location-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; |
|
||||
} |
|
||||
|
|
||||
.inbound-item { |
|
||||
display: flex; |
|
||||
justify-content: space-between; |
|
||||
align-items: center; |
|
||||
padding: 16px; |
|
||||
} |
|
||||
|
|
||||
.product-info { |
|
||||
flex: 1; |
|
||||
} |
|
||||
|
|
||||
.product-name { |
|
||||
font-size: 16px; |
|
||||
font-weight: bold; |
|
||||
color: #323233; |
|
||||
margin-bottom: 4px; |
|
||||
} |
|
||||
|
|
||||
.product-spec { |
|
||||
font-size: 12px; |
|
||||
color: #969799; |
|
||||
margin-bottom: 4px; |
|
||||
} |
|
||||
|
|
||||
.product-quantity { |
|
||||
font-size: 14px; |
|
||||
color: #646566; |
|
||||
} |
|
||||
|
|
||||
.inbound-input { |
|
||||
margin-left: 16px; |
|
||||
} |
|
||||
|
|
||||
.bottom-actions { |
|
||||
position: fixed; |
|
||||
bottom: 0; |
|
||||
left: 0; |
|
||||
right: 0; |
|
||||
padding: 16px; |
|
||||
background: white; |
|
||||
border-top: 1px solid #ebedf0; |
|
||||
} |
|
||||
</style> |
|
||||
@ -1,266 +1,119 @@ |
|||||
<template> |
<template> |
||||
<div class="sales-return-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 class="pda-container"> |
||||
|
<!-- 状态栏 --> |
||||
|
<div class="status-bar"> |
||||
|
<div class="goBack" @click="$router.back()"><i class="el-icon-arrow-left"></i>上一页</div> |
||||
|
<div class="time">销售退货</div> |
||||
|
<div class="network" style="color: #fff" @click="$router.push({path: '/'})">🏠首页</div> |
||||
</div> |
</div> |
||||
|
|
||||
<!-- 退货单列表 --> |
|
||||
<van-pull-refresh v-model="refreshing" @refresh="onRefresh"> |
|
||||
<van-list |
|
||||
v-model="loading" |
|
||||
:finished="finished" |
|
||||
finished-text="没有更多了" |
|
||||
@load="onLoad" |
|
||||
> |
|
||||
|
<!-- 功能菜单 --> |
||||
|
<div class="menu-grid"> |
||||
<div |
<div |
||||
v-for="item in returnList" |
|
||||
:key="item.id" |
|
||||
class="return-item" |
|
||||
@click="handleReturnClick(item)" |
|
||||
|
class="menu-item" |
||||
|
v-for="(btn, index) in buttons" |
||||
|
:key="index" |
||||
|
@click="$router.push(btn.to)" |
||||
> |
> |
||||
<div class="return-header"> |
|
||||
<div class="return-no">{{ item.returnNo }}</div> |
|
||||
<div class="return-status" :class="getStatusClass(item.status)"> |
|
||||
{{ getStatusText(item.status) }} |
|
||||
</div> |
|
||||
</div> |
|
||||
<div class="return-info"> |
|
||||
<div class="info-row"> |
|
||||
<span class="label">原订单号:</span> |
|
||||
<span class="value">{{ item.originalOrderNo }}</span> |
|
||||
</div> |
|
||||
<div class="info-row"> |
|
||||
<span class="label">客户:</span> |
|
||||
<span class="value">{{ item.customerName }}</span> |
|
||||
</div> |
|
||||
<div class="info-row"> |
|
||||
<span class="label">退货原因:</span> |
|
||||
<span class="value">{{ item.returnReason }}</span> |
|
||||
</div> |
|
||||
<div class="info-row"> |
|
||||
<span class="label">退货数量:</span> |
|
||||
<span class="value">{{ item.returnQuantity }}</span> |
|
||||
|
<div class="menu-icon" :class="btn.iconClass"> |
||||
|
<van-icon :name="btn.icon" size="24" /> |
||||
</div> |
</div> |
||||
<div class="info-row"> |
|
||||
<span class="label">退货金额:</span> |
|
||||
<span class="value amount">¥{{ item.returnAmount }}</span> |
|
||||
|
<div class="menu-text">{{ btn.label }}</div> |
||||
</div> |
</div> |
||||
<div class="info-row"> |
|
||||
<span class="label">申请时间:</span> |
|
||||
<span class="value">{{ item.applyTime }}</span> |
|
||||
</div> |
</div> |
||||
</div> |
</div> |
||||
</div> |
|
||||
</van-list> |
|
||||
</van-pull-refresh> |
|
||||
</div> |
|
||||
</template> |
</template> |
||||
|
|
||||
<script> |
<script> |
||||
export default { |
export default { |
||||
name: 'SalesReturn', |
|
||||
data() { |
data() { |
||||
return { |
return { |
||||
searchValue: '', |
|
||||
activeTab: 'all', |
|
||||
refreshing: false, |
|
||||
loading: false, |
|
||||
finished: false, |
|
||||
returnList: [ |
|
||||
{ |
|
||||
id: 1, |
|
||||
returnNo: 'RT202401001', |
|
||||
originalOrderNo: 'SO202401001', |
|
||||
customerName: '客户A有限公司', |
|
||||
returnReason: '质量问题', |
|
||||
returnQuantity: 10, |
|
||||
returnAmount: 25000.00, |
|
||||
applyTime: '2024-01-15 10:30', |
|
||||
status: 0 |
|
||||
}, |
|
||||
{ |
|
||||
id: 2, |
|
||||
returnNo: 'RT202401002', |
|
||||
originalOrderNo: 'SO202401002', |
|
||||
customerName: '客户B有限公司', |
|
||||
returnReason: '规格不符', |
|
||||
returnQuantity: 5, |
|
||||
returnAmount: 12500.00, |
|
||||
applyTime: '2024-01-14 09:20', |
|
||||
status: 1 |
|
||||
}, |
|
||||
{ |
|
||||
id: 3, |
|
||||
returnNo: 'RT202401003', |
|
||||
originalOrderNo: 'SO202401003', |
|
||||
customerName: '客户C有限公司', |
|
||||
returnReason: '数量错误', |
|
||||
returnQuantity: 8, |
|
||||
returnAmount: 18000.00, |
|
||||
applyTime: '2024-01-13 14:15', |
|
||||
status: 2 |
|
||||
} |
|
||||
|
buttons: [ |
||||
|
{ icon: 'shopping-cart-o', label: '入库', iconClass: 'purchase', to: 'salereturn-inbound' }, |
||||
|
{ icon: 'orders-o', label: '报废', iconClass: 'inspection', to: 'salereturn-scrap' } |
||||
] |
] |
||||
} |
} |
||||
}, |
|
||||
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() |
|
||||
}, |
|
||||
handleReturnClick(item) { |
|
||||
if (item.status === 0 || item.status === 1) { |
|
||||
this.$router.push(`/sales-return/receive/${item.returnNo}`) |
|
||||
} else { |
|
||||
this.$toast('该退货单已完成处理') |
|
||||
} |
|
||||
}, |
|
||||
getStatusText(status) { |
|
||||
const statusMap = { |
|
||||
0: '待收货', |
|
||||
1: '质检中', |
|
||||
2: '已完成' |
|
||||
} |
|
||||
return statusMap[status] || '未知' |
|
||||
}, |
|
||||
getStatusClass(status) { |
|
||||
const classMap = { |
|
||||
0: 'status-pending', |
|
||||
1: 'status-checking', |
|
||||
2: 'status-completed' |
|
||||
} |
|
||||
return classMap[status] || '' |
|
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
</script> |
</script> |
||||
|
|
||||
<style scoped> |
|
||||
.sales-return-container { |
|
||||
min-height: 100vh; |
|
||||
background-color: #f7f8fa; |
|
||||
} |
|
||||
|
|
||||
.search-section { |
|
||||
padding: 10px 16px; |
|
||||
background: white; |
|
||||
|
<style> |
||||
|
/* filepath: D:\xjcode\wms-pda\src\views\modules\recv\po-recv.vue */ |
||||
|
:root { |
||||
|
--columns: 3; |
||||
|
--button-size: calc(100vw / var(--columns) - 20px); |
||||
} |
} |
||||
|
|
||||
.filter-section { |
|
||||
background: white; |
|
||||
border-bottom: 1px solid #ebedf0; |
|
||||
} |
|
||||
|
|
||||
.return-item { |
|
||||
background: white; |
|
||||
margin: 10px 16px; |
|
||||
border-radius: 8px; |
|
||||
padding: 16px; |
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); |
|
||||
|
.pda-container { |
||||
|
width: 100vw; |
||||
|
height: 100vh; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
background: #f5f5f5; |
||||
|
font-family: 'Arial', sans-serif; |
||||
|
overflow: auto; |
||||
} |
} |
||||
|
|
||||
.return-header { |
|
||||
|
.status-bar { |
||||
display: flex; |
display: flex; |
||||
justify-content: space-between; |
justify-content: space-between; |
||||
align-items: center; |
align-items: center; |
||||
margin-bottom: 12px; |
|
||||
} |
|
||||
|
|
||||
.return-no { |
|
||||
font-size: 16px; |
|
||||
font-weight: bold; |
|
||||
color: #323233; |
|
||||
|
padding: 10px 20px; |
||||
|
background: #17B3A3; |
||||
} |
} |
||||
|
|
||||
.return-status { |
|
||||
padding: 4px 8px; |
|
||||
border-radius: 4px; |
|
||||
font-size: 12px; |
|
||||
color: white; |
|
||||
} |
|
||||
|
|
||||
.status-pending { |
|
||||
background-color: #ff976a; |
|
||||
} |
|
||||
|
|
||||
.status-checking { |
|
||||
background-color: #1989fa; |
|
||||
|
.menu-grid { |
||||
|
display: grid; |
||||
|
grid-template-columns: repeat(3, 1fr); |
||||
|
gap: 15px; |
||||
|
padding: 20px; |
||||
|
justify-content: center; /* 水平居中 */ |
||||
|
align-content: center; /* 垂直居中 */ |
||||
|
width: 100%; /* 确保占满容器宽度 */ |
||||
} |
} |
||||
|
|
||||
.status-completed { |
|
||||
background-color: #07c160; |
|
||||
|
.menu-item { |
||||
|
background: white; |
||||
|
border-radius: 12px; |
||||
|
padding: 20px 10px; |
||||
|
text-align: center; |
||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); |
||||
|
transition: transform 0.2s; |
||||
} |
} |
||||
|
|
||||
.return-info { |
|
||||
font-size: 14px; |
|
||||
|
.menu-item:active { |
||||
|
transform: scale(0.95); |
||||
} |
} |
||||
|
|
||||
.info-row { |
|
||||
|
.menu-icon { |
||||
|
width: 50px; |
||||
|
height: 50px; |
||||
|
border-radius: 50%; |
||||
display: flex; |
display: flex; |
||||
margin-bottom: 6px; |
|
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
margin: 0 auto 10px; |
||||
|
color: white; |
||||
} |
} |
||||
|
|
||||
.info-row:last-child { |
|
||||
margin-bottom: 0; |
|
||||
|
.menu-icon.purchase { |
||||
|
background: linear-gradient(135deg, #17B3A3 0%, #1dc5ef 100%); |
||||
} |
} |
||||
|
|
||||
.label { |
|
||||
color: #969799; |
|
||||
width: 80px; |
|
||||
flex-shrink: 0; |
|
||||
|
.menu-icon.inspection { |
||||
|
background: linear-gradient(135deg, #17B3A3 0%, #1dc5ef 100%); |
||||
} |
} |
||||
|
|
||||
.value { |
|
||||
color: #323233; |
|
||||
flex: 1; |
|
||||
|
.menu-icon.qualified { |
||||
|
background: linear-gradient(135deg, #17B3A3 0%, #1dc5ef 100%); |
||||
} |
} |
||||
|
|
||||
.value.amount { |
|
||||
color: #ee0a24; |
|
||||
font-weight: bold; |
|
||||
|
.menu-text { |
||||
|
font-size: 13px; |
||||
|
color: #333; |
||||
|
font-weight: bold; /* 加粗字体 */ |
||||
|
white-space: nowrap; /* 防止文字换行 */ |
||||
|
overflow: hidden; |
||||
|
text-overflow: ellipsis; |
||||
} |
} |
||||
</style> |
</style> |
||||
@ -0,0 +1,241 @@ |
|||||
|
<template> |
||||
|
<div> |
||||
|
<div class="pda-container"> |
||||
|
<div class="status-bar"> |
||||
|
<div class="goBack" @click="handleBack"><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 class="main-content"> |
||||
|
<!-- RMA号输入/扫描 --> |
||||
|
<el-input v-model="scanRma" placeholder="扫描RMA条码或输入RMA号" @keyup.enter.native="searchRmaList" ref="scanRmaRef" class="rma-input" /> |
||||
|
<!-- RMA明细选择 --> |
||||
|
<div v-if="rmaList.length > 0" class="rma-list"> |
||||
|
<el-form> |
||||
|
<el-row v-for="(rmaDetail, index) in rmaList[0].detailList" :key="index" :class="index < rmaList.length - 1 ? 'bottom-line-row' : ''"> |
||||
|
<el-col :span="8"> |
||||
|
<el-form-item label="物料"><span>{{ rmaDetail.partNo }}</span></el-form-item> |
||||
|
</el-col> |
||||
|
<el-col :span="8"> |
||||
|
<el-form-item label="批号"><span>{{ rmaDetail.batchNo }}</span></el-form-item> |
||||
|
</el-col> |
||||
|
<el-col :span="8"> |
||||
|
<el-form-item label="数量"><span>{{ rmaDetail.processQty }}</span></el-form-item> |
||||
|
</el-col> |
||||
|
<el-col :span="24"> |
||||
|
<el-button type="primary" size="mini" @click="addToReturnList(rmaDetail)" :disabled="isInReturnList(rmaDetail)">添加到退货明细</el-button> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
</el-form> |
||||
|
</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> |
||||
|
<el-form label-position="top" class="detail-form"> |
||||
|
<el-row :gutter="10"> |
||||
|
<el-col :span="12"><el-form-item label="物料"><el-input v-model="detail.partNo" size="small" /></el-form-item></el-col> |
||||
|
<el-col :span="12"><el-form-item label="数量"><el-input v-model="detail.processQty" type="number" size="small" /></el-form-item></el-col> |
||||
|
<el-col :span="12"><el-form-item label="批号"><el-input v-model="detail.batchNo" size="small" /></el-form-item></el-col> |
||||
|
<el-col :span="12"><el-form-item label="库位"><el-input v-model="detail.locationNo" size="small" /></el-form-item></el-col> |
||||
|
<el-col :span="12"><el-form-item label="仓库"><el-input v-model="detail.warehouseId" size="small" /></el-form-item></el-col> |
||||
|
</el-row> |
||||
|
<!-- 包装单元 --> |
||||
|
<div class="pack-unit-area"> |
||||
|
<div class="pack-title">包装单元</div> |
||||
|
<div v-for="(pack, pIdx) in detail.packUnitList" :key="pIdx" class="pack-block"> |
||||
|
<el-row :gutter="10"> |
||||
|
<el-col :span="6"><el-input v-model="pack.code" placeholder="编码" size="small" /></el-col> |
||||
|
<el-col :span="6"><el-input v-model="pack.qty" placeholder="数量" type="number" size="small" /></el-col> |
||||
|
<el-col :span="6"><el-input v-model="pack.perQty" placeholder="单包数量" type="number" size="small" /></el-col> |
||||
|
<el-col :span="4"><el-input v-model="pack.packageQty" placeholder="包数" type="number" size="small" /></el-col> |
||||
|
<el-col :span="2"> |
||||
|
<el-button type="danger" icon="el-icon-delete" circle size="mini" @click="removePackUnit(detail, pIdx)"></el-button> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
</div> |
||||
|
<el-button type="primary" size="mini" icon="el-icon-plus" @click="addPackUnit(detail)" class="add-pack-btn">添加包装单元</el-button> |
||||
|
</div> |
||||
|
</el-form> |
||||
|
</el-card> |
||||
|
</div> |
||||
|
<el-button type="success" class="submit-btn" @click="submitReturn">提交</el-button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { getRmaList, processReturn } from "@/api/sales-return/sales-return.js"; |
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
scanRma: "", |
||||
|
rmaList: [], |
||||
|
returnList: [] // 退货明细列表 |
||||
|
}; |
||||
|
}, |
||||
|
methods: { |
||||
|
handleBack() { |
||||
|
this.$router.back(); |
||||
|
}, |
||||
|
searchRmaList() { |
||||
|
if (!this.scanRma) return (this.rmaList = []); |
||||
|
getRmaList({ rmaNo: this.scanRma }).then(({ data }) => { |
||||
|
if (data.code === 0) this.rmaList = data.rows; |
||||
|
}); |
||||
|
}, |
||||
|
addToReturnList(rmaDetail) { |
||||
|
// 避免重复添加 |
||||
|
if (this.isInReturnList(rmaDetail)) return; |
||||
|
this.returnList.push({ |
||||
|
site: "1", |
||||
|
partNo: rmaDetail.partNo, |
||||
|
processQty: rmaDetail.processQty, |
||||
|
batchNo: rmaDetail.batchNo, |
||||
|
locationNo: "", |
||||
|
warehouseId: "", |
||||
|
packUnitList: [] |
||||
|
}); |
||||
|
}, |
||||
|
isInReturnList(rmaDetail) { |
||||
|
return this.returnList.some( |
||||
|
d => d.partNo === rmaDetail.partNo && d.batchNo === rmaDetail.batchNo |
||||
|
); |
||||
|
}, |
||||
|
removeDetail(idx) { |
||||
|
this.returnList.splice(idx, 1); |
||||
|
}, |
||||
|
addPackUnit(detail) { |
||||
|
detail.packUnitList.push({code: "", qty: '', perQty: '', packageQty: ''}); |
||||
|
}, |
||||
|
removePackUnit(detail, idx) { |
||||
|
detail.packUnitList.splice(idx, 1); |
||||
|
}, |
||||
|
submitReturn() { |
||||
|
if (!this.scanRma || this.returnList.length === 0) { |
||||
|
this.$message.error("请先扫描RMA号并选择退货明细"); |
||||
|
return; |
||||
|
} |
||||
|
processReturn({ |
||||
|
rmaNo: this.scanRma, |
||||
|
processType: "inbound", |
||||
|
detailList: this.returnList |
||||
|
}).then(({data}) => { |
||||
|
if (data.code === 0) { |
||||
|
this.$message.success("操作成功"); |
||||
|
this.scanRma = ""; |
||||
|
this.rmaList = []; |
||||
|
this.returnList = []; |
||||
|
} else { |
||||
|
this.$message.error(data.msg); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}, |
||||
|
mounted() { |
||||
|
this.$nextTick(() => this.$refs.scanRmaRef && this.$refs.scanRmaRef.focus()); |
||||
|
} |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.pda-container { |
||||
|
background: #f5f5f5; |
||||
|
min-height: 100vh; |
||||
|
} |
||||
|
|
||||
|
.status-bar { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
background: #409EFF; |
||||
|
color: #fff; |
||||
|
padding: 8px 12px; |
||||
|
} |
||||
|
|
||||
|
.goBack { |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
|
||||
|
.main-content { |
||||
|
padding: 10px 6px 30px 6px; |
||||
|
} |
||||
|
|
||||
|
.rma-input { |
||||
|
margin-bottom: 12px; |
||||
|
} |
||||
|
|
||||
|
.rma-list { |
||||
|
margin-bottom: 18px; |
||||
|
background: #fff; |
||||
|
border-radius: 8px; |
||||
|
box-shadow: 0 2px 8px #e6e6e6; |
||||
|
padding: 10px; |
||||
|
} |
||||
|
|
||||
|
.return-list-title { |
||||
|
font-size: 16px; |
||||
|
font-weight: bold; |
||||
|
color: #409EFF; |
||||
|
margin: 12px 0 8px 0; |
||||
|
} |
||||
|
|
||||
|
.detail-block { |
||||
|
margin-bottom: 18px; |
||||
|
} |
||||
|
|
||||
|
.detail-card { |
||||
|
border-radius: 10px; |
||||
|
box-shadow: 0 2px 12px #e0e7ef; |
||||
|
} |
||||
|
|
||||
|
.detail-header { |
||||
|
font-weight: bold; |
||||
|
font-size: 15px; |
||||
|
color: #333; |
||||
|
border-left: 4px solid #409EFF; |
||||
|
padding-left: 8px; |
||||
|
} |
||||
|
|
||||
|
.detail-form { |
||||
|
padding: 6px 0 0 0; |
||||
|
} |
||||
|
|
||||
|
.pack-unit-area { |
||||
|
background: #f8fafd; |
||||
|
border-radius: 6px; |
||||
|
padding: 8px 6px 6px 6px; |
||||
|
margin-top: 8px; |
||||
|
} |
||||
|
|
||||
|
.pack-title { |
||||
|
font-weight: bold; |
||||
|
color: #666; |
||||
|
margin-bottom: 4px; |
||||
|
} |
||||
|
|
||||
|
.pack-block { |
||||
|
margin-bottom: 6px; |
||||
|
} |
||||
|
|
||||
|
.add-pack-btn { |
||||
|
margin-top: 4px; |
||||
|
} |
||||
|
|
||||
|
.submit-btn { |
||||
|
width: 100%; |
||||
|
font-size: 16px; |
||||
|
margin-top: 18px; |
||||
|
} |
||||
|
|
||||
|
.bottom-line-row { |
||||
|
border-bottom: 1px solid #eee; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,133 @@ |
|||||
|
<template> |
||||
|
<div> |
||||
|
<div class="pda-container"> |
||||
|
<div class="status-bar"> |
||||
|
<div class="goBack" @click="handleBack"><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 class="main-content"> |
||||
|
<!-- RMA号输入/扫描 --> |
||||
|
<el-input v-model="scanRma" placeholder="扫描RMA条码或输入RMA号" @keyup.enter.native="searchRmaList" ref="scanRmaRef" class="rma-input" /> |
||||
|
<!-- RMA明细选择 --> |
||||
|
<div v-if="rmaList.length > 0" class="rma-list"> |
||||
|
<el-form> |
||||
|
<el-row v-for="(rmaDetail, index) in rmaList[0].detailList" :key="index" :class="index < rmaList.length - 1 ? 'bottom-line-row' : ''"> |
||||
|
<el-col :span="8"><el-form-item label="物料"><span>{{ rmaDetail.partNo }}</span></el-form-item></el-col> |
||||
|
<el-col :span="8"><el-form-item label="批号"><span>{{ rmaDetail.batchNo }}</span></el-form-item></el-col> |
||||
|
<el-col :span="8"><el-form-item label="数量"><span>{{ rmaDetail.processQty }}</span></el-form-item></el-col> |
||||
|
<el-col :span="24"><el-form-item label="描述"><span>{{ rmaDetail.desc }}</span></el-form-item></el-col> |
||||
|
<el-col :span="24"> |
||||
|
<el-button type="primary" size="mini" @click="addToReturnList(rmaDetail)" :disabled="isInReturnList(rmaDetail)">添加到退货明细</el-button> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
</el-form> |
||||
|
</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> |
||||
|
<el-form label-position="top" class="detail-form"> |
||||
|
<el-row :gutter="10"> |
||||
|
<el-col :span="12"><el-form-item label="物料"><el-input v-model="detail.partNo" size="small" /></el-form-item></el-col> |
||||
|
<el-col :span="12"><el-form-item label="数量"><el-input v-model="detail.processQty" type="number" size="small" /></el-form-item></el-col> |
||||
|
<el-col :span="12"><el-form-item label="批号"><el-input v-model="detail.batchNo" size="small" /></el-form-item></el-col> |
||||
|
<el-col :span="12"><el-form-item label="库位"><el-input v-model="detail.locationNo" size="small" /></el-form-item></el-col> |
||||
|
<el-col :span="12"><el-form-item label="仓库"><el-input v-model="detail.warehouseId" size="small" /></el-form-item></el-col> |
||||
|
</el-row> |
||||
|
</el-form> |
||||
|
</el-card> |
||||
|
</div> |
||||
|
<el-button type="success" class="submit-btn" @click="submitReturn">提交</el-button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { getRmaList, processReturn } from "@/api/sales-return/sales-return.js"; |
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
scanRma: "", |
||||
|
rmaList: [], |
||||
|
returnList: [] |
||||
|
}; |
||||
|
}, |
||||
|
methods: { |
||||
|
handleBack() { |
||||
|
this.$router.back(); |
||||
|
}, |
||||
|
searchRmaList() { |
||||
|
if (!this.scanRma) return (this.rmaList = []); |
||||
|
getRmaList({ rmaNo: this.scanRma }).then(({ data }) => { |
||||
|
if (data.code === 0) this.rmaList = data.rows; |
||||
|
}); |
||||
|
}, |
||||
|
addToReturnList(rmaDetail) { |
||||
|
if (this.isInReturnList(rmaDetail)) return; |
||||
|
this.returnList.push({ |
||||
|
site: "1", |
||||
|
partNo: rmaDetail.partNo, |
||||
|
processQty: rmaDetail.processQty, |
||||
|
batchNo: rmaDetail.batchNo, |
||||
|
locationNo: "", |
||||
|
warehouseId: "" |
||||
|
}); |
||||
|
}, |
||||
|
isInReturnList(rmaDetail) { |
||||
|
return this.returnList.some( |
||||
|
d => d.partNo === rmaDetail.partNo && d.batchNo === rmaDetail.batchNo |
||||
|
); |
||||
|
}, |
||||
|
removeDetail(idx) { |
||||
|
this.returnList.splice(idx, 1); |
||||
|
}, |
||||
|
submitReturn() { |
||||
|
if (!this.scanRma || this.returnList.length === 0) { |
||||
|
this.$message.error("请先扫描RMA号并选择退货明细"); |
||||
|
return; |
||||
|
} |
||||
|
processReturn({ |
||||
|
rmaNo: this.scanRma, |
||||
|
processType: "scrap", |
||||
|
detailList: this.returnList |
||||
|
}).then(({ data }) => { |
||||
|
if (data.code === 0) { |
||||
|
this.$message.success("操作成功"); |
||||
|
this.scanRma = ""; |
||||
|
this.rmaList = []; |
||||
|
this.returnList = []; |
||||
|
} else { |
||||
|
this.$message.error(data.msg); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}, |
||||
|
mounted() { |
||||
|
this.$nextTick(() => this.$refs.scanRmaRef && this.$refs.scanRmaRef.focus()); |
||||
|
} |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.pda-container { background: #f5f5f5; min-height: 100vh; } |
||||
|
.status-bar { display: flex; justify-content: space-between; align-items: center; background: #409EFF; color: #fff; padding: 8px 12px; } |
||||
|
.goBack { cursor: pointer; } |
||||
|
.main-content { padding: 10px 6px 30px 6px; } |
||||
|
.rma-input { margin-bottom: 12px; } |
||||
|
.rma-list { margin-bottom: 18px; background: #fff; border-radius: 8px; box-shadow: 0 2px 8px #e6e6e6; padding: 10px; } |
||||
|
.return-list-title { font-size: 16px; font-weight: bold; color: #409EFF; margin: 12px 0 8px 0; } |
||||
|
.detail-block { margin-bottom: 18px; } |
||||
|
.detail-card { border-radius: 10px; box-shadow: 0 2px 12px #e0e7ef; } |
||||
|
.detail-header { font-weight: bold; font-size: 15px; color: #333; border-left: 4px solid #409EFF; padding-left: 8px; } |
||||
|
.detail-form { padding: 6px 0 0 0; } |
||||
|
.submit-btn { width: 100%; font-size: 16px; margin-top: 18px; } |
||||
|
.bottom-line-row { border-bottom: 1px solid #eee; } |
||||
|
</style> |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue