pda-uni-app-perkins/pages/stock-in/engine.vue

433 lines
10 KiB
Vue
Raw Permalink 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="standId"
:focus="focusedField === 'stand'"
@input="handleStandInput"
@blur="handleBlur('stand')" />
</view>
</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"
:focus="focusedField === 'vehicle'"
@input="handleVehicleInput"
@blur="handleBlur('vehicle')" />
</view>
</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="model"
:focus="focusedField === 'model'"
@input="handleModelInput"
@blur="handleBlur('model')" />
</view>
</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="goodsId"
:focus="focusedField === 'goods'"
@input="handleGoodsInput"
@blur="handleBlur('goods')" />
</view>
</view>
<button class="submit" :disabled="loading" @click="submit">
<i class="icon icon-save" style="margin-right:12rpx"></i> 确认入库
</button>
</view>
</view>
</template>
<script>
import {
WmsApiClient
} from '@/common/wmsApi.js';
import {
DialogUtils
} from '@/utils/dialog.js';
export default {
data() {
return {
standId: '',
vehicleNo: '',
goodsId: '',
model: '',
loading: false,
focusedField: '',
};
},
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;
},
resetForm() {
this.standId = '';
this.vehicleNo = '';
this.goodsId = '';
this.model = '';
this.focusedField = '';
},
async submit() {
// 立即检查 loading 状态,防止重复提交
if (this.loading) return;
const stand = (this.standId || '').trim();
const vehicle = (this.vehicleNo || '').trim();
const goods = (this.goodsId || '').trim();
const model = (this.model || '').trim();
// 必填验证
if (!stand) {
DialogUtils.showWarningMessage('提示', '请先输入入库口');
return;
}
if (!vehicle) {
DialogUtils.showWarningMessage('提示', '请输入母托号');
return;
}
if (!goods) {
DialogUtils.showWarningMessage('提示', '请输入发动机序列号');
return;
}
if (!model) {
DialogUtils.showWarningMessage('提示', '请输入发动机机型');
return;
}
// 防呆验证:发动机机型和序列号不能完全相同
if (!this.validateEngineData(model, goods)) {
this.goodsId = '';
return;
}
// 显示确认弹窗
const confirmed = await this.showConfirmDialog();
if (!confirmed) {
return;
}
// 二次检查,防御性编程
if (this.loading) return;
this.loading = true;
uni.showLoading({
title: '正在提交',
mask: true
});
try {
const response = await WmsApiClient.engineStockIn({
standId: stand,
vehicleNo: vehicle,
goodsId: goods,
model,
});
const code = Number(response.code);
if (code === 0) {
DialogUtils.showSuccessMessage('成功', '入库成功');
this.resetForm();
} else {
DialogUtils.showWarningMessage('操作未成功', `${response.message || '提交失败'} (${response.code})`);
}
} catch (error) {
DialogUtils.showErrorMessage('错误', this.resolveError(error));
} finally {
this.loading = false;
uni.hideLoading();
}
},
/**
* 验证发动机机型和序列号是否合法
* @param {string} model - 发动机机型
* @param {string} goods - 发动机序列号
* @returns {boolean} 是否通过验证
*/
validateEngineData(model, goods) {
if (model === goods) {
DialogUtils.showErrorMessage('错误', '发动机机型和序列号不能完全相同,请检查扫码结果');
return false;
}
return true;
},
/**
* 显示确认弹窗
* @returns {Promise<boolean>} 用户是否确认提交
*/
showConfirmDialog() {
const content = `入库口:${this.standId}\n母托号${this.vehicleNo}\n发动机机型${this.model}\n发动机序列号${this.goodsId}`;
return DialogUtils.showConfirm(
'确认入库',
content,
'确认提交',
'取消'
);
},
/**
* 处理入库口输入事件
* 当输入有值时自动跳转到下一个字段
*/
handleStandInput() {
if (this.standId && this.standId.trim()) {
this.$nextTick(() => {
this.focusNextField('vehicle');
});
}
},
/**
* 处理母托号输入事件
* 当输入有值时自动跳转到下一个字段
*/
handleVehicleInput() {
if (this.vehicleNo && this.vehicleNo.trim()) {
this.$nextTick(() => {
this.focusNextField('model');
});
}
},
/**
* 处理发动机机型输入事件
* 当输入有值时自动跳转到下一个字段
*/
handleModelInput() {
if (this.model && this.model.trim()) {
this.$nextTick(() => {
this.focusNextField('goods');
});
}
},
/**
* 处理发动机序列号输入事件
* 实时检查防呆,然后验证所有字段
*/
handleGoodsInput() {
if (this.goodsId && this.goodsId.trim()) {
const goods = this.goodsId.trim();
const model = (this.model || '').trim();
// 实时防呆检查:序列号与机型不能相同
if (model && goods === model) {
DialogUtils.showWarningMessage('提示', '发动机序列号与机型不能相同,序列号已清空,请重新扫描');
this.goodsId = '';
return;
}
this.$nextTick(() => {
this.validateAllFields();
});
}
},
/**
* 跳转焦点到指定字段
* @param {string} fieldName - 字段名称 (stand/vehicle/model/goods)
*/
focusNextField(fieldName) {
this.focusedField = fieldName;
},
/**
* 处理输入框失焦事件
* 延迟清空焦点状态,避免跳转时闪烁
* @param {string} fieldName - 失焦的字段名称
*/
handleBlur(fieldName) {
setTimeout(() => {
// 只在当前焦点字段匹配时才清空
if (this.focusedField === fieldName) {
this.focusedField = '';
}
}, 100);
},
/**
* 验证所有字段是否已填写
* 所有字段填写完毕时显示成功提示
*/
validateAllFields() {
const stand = (this.standId || '').trim();
const vehicle = (this.vehicleNo || '').trim();
const model = (this.model || '').trim();
const goods = (this.goodsId || '').trim();
if (stand && vehicle && model && goods) {
// 检查防呆验证
if (!this.validateEngineData(model, goods)) {
return;
}
// 使用 Toast 轻提示,不打断用户操作
DialogUtils.toast('所有字段已填写完成,可提交', 'success', 1500);
}
},
},
};
</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;
}
.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: 20rpx;
}
.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;
}
.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(5, 220, 239, .25);
text-align: center;
margin-top: 28rpx;
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>