pda-uni-app-perkins/pages/stock-in/raw.vue
2025-10-31 22:49:51 +08:00

512 lines
13 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="container">
<view class="header">
<button class="back" @click="back"><i class="icon icon-arrow_back" style="color:#05DCEF;font-size:36rpx"></i></button>
<text class="title">原材料入库</text>
</view>
<view class="content">
<view class="tip">
<view class="tip-icon"><i class="icon icon-info" style="color:#05DCEF"></i></view>
<text class="tip-text">请输入母托号并逐个绑定原材料最后确认入库</text>
</view>
<view class="field">
<text class="label">母托号 <text class="required">*</text></text>
<view class="input-wrap">
<i class="left-icon icon icon-qr_code_2" style="font-size:36rpx"></i>
<input class="input" placeholder="请扫描或输入母托号" v-model="vehicleNo" />
</view>
</view>
<view class="field">
<text class="label">原材料号</text>
<view class="input-wrap">
<i class="left-icon icon icon-qr_code_2" style="font-size:36rpx"></i>
<input class="input" placeholder="请扫描或输入原材料号" v-model="goodsId" />
</view>
</view>
<button class="action-button" :disabled="isBusy" @click="handleNextMaterial">
<i class="icon icon-arrow_forward" style="margin-right:10rpx"></i> 下一颗物料
</button>
<view class="field">
<text class="label">入库口 <text class="required">*</text></text>
<view class="input-wrap">
<i class="left-icon icon icon-qr_code_2" style="font-size:36rpx"></i>
<input class="input" placeholder="请扫描或输入入库口" v-model="standId" />
</view>
</view>
<view class="list-section">
<view class="list-header">
<text class="list-title">已绑定的原材料</text>
<text class="list-count">({{ orderInList.length }})</text>
</view>
<view v-if="!orderInList.length" class="empty-placeholder">
<i class="icon icon-info" style="margin-right:8rpx;color:#05DCEF"></i>
<text>暂无数据</text>
</view>
<view v-else class="card-group">
<view v-for="item in orderInList" :key="item.rowId" class="card">
<view class="card-header">
<view class="card-header-text">
<text class="card-title">物料号:{{ item.goodsId }}</text>
<text class="card-subtitle">数量:{{ formatNumber(item.goodsNum) }}</text>
</view>
<button class="card-action" :disabled="isBusy" @click="deleteItem(item)">
<i class="icon icon-delete" style="color:#ff4d4f;font-size:32rpx"></i>
</button>
</view>
<view class="card-row">
<text class="card-label">批次:</text>
<text class="card-value">{{ item.batchNo || '-' }}</text>
</view>
<view class="card-row">
<text class="card-label">生产日期:</text>
<text class="card-value">{{ formatDate(item.productionDate) }}</text>
</view>
<view class="card-row">
<text class="card-label">机型:</text>
<text class="card-value">{{ item.model || '-' }}</text>
</view>
</view>
</view>
</view>
<button class="submit" :disabled="isBusy" @click="submitRawIn">
<i class="icon icon-task_alt" style="margin-right:10rpx"></i> 确认入库
</button>
</view>
</view>
</template>
<script>
import { WmsApiClient } from '@/common/wmsApi.js';
import { DialogUtils } from '@/utils/dialog.js';
export default {
data() {
return {
vehicleNo: '',
goodsId: '',
standId: '',
orderInList: [],
loadingAction: '',
};
},
computed: {
isBusy() {
return this.loadingAction !== '';
},
},
methods: {
back() {
uni.navigateBack();
},
resolveError(error, fallback = '请求失败') {
if (!error) return fallback;
if (typeof error === 'string') return error;
if (typeof error.message === 'string') return error.message;
if (typeof error.errMsg === 'string') return error.errMsg;
if (typeof error.code === 'number') return `${fallback} (${error.code})`;
return fallback;
},
formatNumber(value) {
const num = Number(value);
if (Number.isNaN(num)) return '0';
return num % 1 === 0 ? String(num) : num.toFixed(2);
},
formatDate(value) {
if (!value) return '-';
const parsed = new Date(value);
if (!Number.isNaN(parsed.getTime())) {
const y = parsed.getFullYear();
const m = String(parsed.getMonth() + 1).padStart(2, '0');
const d = String(parsed.getDate()).padStart(2, '0');
return `${y}-${m}-${d}`;
}
const normalized = value.replace('T', ' ').split(' ')[0];
return normalized || '-';
},
async handleNextMaterial() {
const vehicle = (this.vehicleNo || '').trim();
const goods = (this.goodsId || '').trim();
if (!vehicle) {
DialogUtils.showWarningMessage('提示', '请输入母托号');
return;
}
if (this.isBusy) return;
if (goods) {
this.loadingAction = 'binding';
uni.showLoading({ title: '正在绑定物料', mask: true });
try {
const bindRes = await WmsApiClient.bindVehicle({
vehicleNo: vehicle,
goodsId: goods,
});
const code = Number(bindRes.code);
if (code === 0) {
DialogUtils.showSuccessMessage('成功', '物料绑定成功');
this.goodsId = '';
} else {
DialogUtils.showWarningMessage('绑定未成功', `${bindRes.message || '操作失败'} (${bindRes.code})`);
}
} catch (error) {
DialogUtils.showErrorMessage('错误', this.resolveError(error));
} finally {
uni.hideLoading();
this.loadingAction = '';
}
}
this.loadingAction = 'query';
uni.showLoading({ title: '获取物料列表', mask: true });
try {
await this.loadOrderList(vehicle);
} catch (error) {
DialogUtils.showErrorMessage('错误', this.resolveError(error));
} finally {
uni.hideLoading();
this.loadingAction = '';
}
},
async loadOrderList(vehicle) {
const listRes = await WmsApiClient.getOrderInWithVehicleNo({ vehicleNo: vehicle });
const code = Number(listRes.code);
if (code === 0) {
const data = Array.isArray(listRes.returnData) ? listRes.returnData : [];
this.orderInList = data.map((item) => ({
...item,
goodsNum: item.goodsNum,
}));
} else {
DialogUtils.showWarningMessage('提示', `${listRes.message || '获取物料列表失败'} (${listRes.code})`);
}
},
async deleteItem(item) {
const vehicle = (this.vehicleNo || '').trim();
if (!vehicle) {
DialogUtils.showWarningMessage('提示', '母托号不能为空');
return;
}
if (!item || !item.rowId) return;
if (this.isBusy) return;
this.loadingAction = 'deleting';
uni.showLoading({ title: '正在删除', mask: true });
try {
const res = await WmsApiClient.deleteOrderIn({ rowId: item.rowId });
const code = Number(res.code);
if (code === 0) {
DialogUtils.showSuccessMessage('成功', '物料删除成功');
await this.loadOrderList(vehicle);
} else {
DialogUtils.showWarningMessage('删除未成功', `${res.message || '删除失败'} (${res.code})`);
}
} catch (error) {
DialogUtils.showErrorMessage('错误', this.resolveError(error));
} finally {
uni.hideLoading();
this.loadingAction = '';
}
},
async submitRawIn() {
const vehicle = (this.vehicleNo || '').trim();
const stand = (this.standId || '').trim();
if (!vehicle) {
DialogUtils.showWarningMessage('提示', '请输入母托号');
return;
}
if (!stand) {
DialogUtils.showWarningMessage('提示', '请输入入库口');
return;
}
if (!this.orderInList.length) {
DialogUtils.showWarningMessage('提示', '请先绑定原材料');
return;
}
if (this.isBusy) return;
this.loadingAction = 'submitting';
uni.showLoading({ title: '正在提交', mask: true });
try {
const res = await WmsApiClient.rawStockIn({ vehicleNo: vehicle, standId: stand });
const code = Number(res.code);
if (code === 0) {
DialogUtils.showSuccessMessage('成功', '原材料入库成功');
this.vehicleNo = '';
this.goodsId = '';
this.standId = '';
this.orderInList = [];
} else {
DialogUtils.showWarningMessage('入库未成功', `${res.message || '提交失败'} (${res.code})`);
}
} catch (error) {
DialogUtils.showErrorMessage('错误', this.resolveError(error));
} finally {
uni.hideLoading();
this.loadingAction = '';
}
},
},
};
</script>
<style scoped>
.header {
position: relative;
height: 120rpx;
padding: 0;
background: linear-gradient(90deg, var(--grad-primary-start), var(--grad-primary-mid));
display: flex;
align-items: center;
justify-content: center;
}
.container {
min-height: 100vh;
background: #F5F5F5;
}
.title {
color: #fff;
font-size: 32rpx;
font-weight: 600;
}
.content {
padding: 24rpx;
padding-bottom: 56rpx;
}
.tip {
flex-direction: row;
display: flex;
align-items: center;
background: #f2fdff;
border-radius: 12rpx;
padding: 24rpx;
color: #333;
margin-bottom: 28rpx;
}
.tip-icon {
width: 40rpx;
height: 40rpx;
border-radius: 20rpx;
background: #e6f7ff;
text-align: center;
line-height: 40rpx;
margin-right: 12rpx;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
}
.tip-text {
color: #333;
font-size: 26rpx;
}
.field {
margin-bottom: 24rpx;
}
.label {
color: #333;
font-size: 30rpx;
margin-bottom: 12rpx;
display: block;
}
.required {
color: #ff4d4f;
}
.input-wrap {
height: 96rpx;
border-radius: 12rpx;
border: 1px solid #e6e6e6;
background: #fff;
padding: 0 20rpx;
display: flex;
align-items: center;
}
.left-icon {
color: #05DCEF;
font-size: 36rpx;
margin-right: 12rpx;
}
.input {
flex: 1;
height: 100%;
font-size: 30rpx;
}
.action-button {
width: 100%;
height: 92rpx;
border: none;
color: #fff;
border-radius: 46rpx;
background: linear-gradient(90deg, var(--grad-primary-start), var(--grad-primary-mid));
font-size: 32rpx;
box-shadow: 0 8rpx 22rpx rgba(5, 220, 239, .25);
text-align: center;
margin-bottom: 28rpx;
display: flex;
align-items: center;
justify-content: center;
}
.action-button[disabled] {
opacity: .6;
}
.list-section {
margin-top: 12rpx;
margin-bottom: 32rpx;
}
.list-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16rpx;
}
.list-title {
font-size: 30rpx;
font-weight: 600;
color: #333;
}
.list-count {
font-size: 28rpx;
color: #888;
}
.empty-placeholder {
height: 140rpx;
border-radius: 12rpx;
border: 1px dashed #d9d9d9;
color: #999;
background: #fff;
display: flex;
align-items: center;
justify-content: center;
font-size: 28rpx;
}
.card-group {
display: flex;
flex-direction: column;
gap: 20rpx;
}
.card {
background: #fff;
border-radius: 16rpx;
padding: 24rpx;
box-shadow: 0 8rpx 22rpx rgba(0, 0, 0, .05);
}
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12rpx;
}
.card-header-text {
display: flex;
flex-direction: column;
gap: 8rpx;
}
.card-title {
font-size: 30rpx;
color: #111;
font-weight: 600;
}
.card-subtitle {
font-size: 26rpx;
color: #555;
}
.card-action {
width: 64rpx;
height: 64rpx;
border-radius: 32rpx;
border: none;
background: rgba(255, 77, 79, .12);
display: flex;
align-items: center;
justify-content: center;
}
.card-action[disabled] {
opacity: .4;
}
.card-row {
display: flex;
align-items: center;
margin-top: 6rpx;
}
.card-label {
font-size: 26rpx;
color: #666;
margin-right: 8rpx;
}
.card-value {
font-size: 26rpx;
color: #333;
}
.submit {
width: 100%;
height: 92rpx;
border: none;
color: #fff;
border-radius: 46rpx;
background: linear-gradient(90deg, var(--grad-primary-start), var(--grad-primary-mid));
font-size: 32rpx;
box-shadow: 0 8rpx 22rpx rgba(52, 199, 89, .25);
text-align: center;
display: flex;
align-items: center;
justify-content: center;
}
.submit[disabled] {
opacity: .6;
}
.back {
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 80rpx;
height: 64rpx;
line-height: 64rpx;
border: none;
color: #05DCEF;
background: #fff;
border-radius: 12rpx;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
}
</style>