中航光电PDA端
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

1035 lines
24 KiB

<template>
<ifrm>
<uni-forms ref="wrForm" class="formBox" label-position="top">
<uni-forms-item label="扫描模式:" label-width="100px" class="label-left">
<template #label>
<view class="required-label">
<text>扫描模式</text>
</view>
</template>
<view class="mode-switch">
<view
class="switch-btn"
:class="{ active: scanMode === 'bind' }"
@click="handleModeChange('bind')"
>
送料
</view>
<view
class="switch-btn"
:class="{ active: scanMode === 'unbind' }"
@click="handleModeChange('unbind')"
>
叫料
</view>
</view>
</uni-forms-item>
<!-- 1. 物料箱条码 (使用 oneInput) -->
<uni-forms-item label="物料箱条码:" label-width="100px">
<template #label>
<view class="required-label">
<text>物料箱条码</text>
</view>
</template>
<view class="bottom-input-row">
<view class="weight-input-wrapper">
<view class="input-box">
<input
type="text"
v-model="oneInput"
@input="oneInputBlur"
placeholder="物料箱条码"
class="uni-input-border"
confirm-type="done"
:focus="boxInputFocus"
:key="'box-' + boxInputFocus"
/>
</view>
</view>
</view>
</uni-forms-item>
<!-- 2. 起点站点/区域 (使用 twoInput) -->
<uni-forms-item
label="起点站点:"
label-width="100px"
v-if="scanMode === 'bind'"
>
<template #label>
<view class="required-label">
<text class="required-mark">*</text>
<text>起点区域:</text>
</view>
</template>
<input
type="text"
v-model="twoInput"
@input="twoInputBlur"
placeholder="站点码"
class="uni-input-border"
confirm-type="done"
:focus="startInputFocus"
:key="'start-' + startInputFocus"
/>
<view v-if="startData" class="start-confirm">
<text class="check-icon">✓</text>
<text class="confirm-text">已确认: {{ startData.stationName }}</text>
</view>
</uni-forms-item>
<!-- 叫料模式下的起点选择 (保持 Picker 不变,但逻辑需适配) -->
<uni-forms-item label="起点区域:" label-width="100px" v-else>
<template #label>
<view class="required-label">
<text class="required-mark">*</text>
<text>起点区域:</text>
</view>
</template>
<picker
mode="selector"
:range="startAreas"
range-key="name"
:value="startAreaIndex"
@change="handleStartAreaChange"
class="uni-input-border"
disabled
>
<view class="picker-input">
<text :class="startArea ? 'picker-value' : 'picker-placeholder'">
{{ startArea || "请选择起点区域" }}
</text>
</view>
</picker>
<picker
mode="selector"
:range="startStations"
range-key="name"
:value="startStationIndex"
@change="handleStartStationChange"
class="uni-input-border"
:disabled="!startArea"
>
<view class="picker-input">
<text :class="startStation ? 'picker-value' : 'picker-placeholder'">
{{ startStation || "请选择起点站点" }}
</text>
</view>
</picker>
<text style="color: red" v-if="startStations.length <= 0"
>暂无可配送物料</text
>
</uni-forms-item>
<!-- 3. 终点区域 (使用 therrInput 用于叫料模式的扫码) -->
<uni-forms-item
label="终点区域:"
label-width="150px"
v-if="scanMode === 'bind'"
>
<template #label>
<view class="required-label">
<text class="required-mark">*</text>
<text>终点区域:</text>
</view>
</template>
<picker
mode="selector"
:range="workCenters"
range-key="name"
:value="endCenterIndex"
@change="handleEndChange"
class="uni-input-border"
>
<view class="picker-input">
<text :class="endCenter ? 'picker-value' : 'picker-placeholder'">
{{ endCenter || "请选择作业中心" }}
</text>
</view>
</picker>
<picker
v-if="endAreas && endAreas.length > 0"
mode="selector"
:range="endAreas"
range-key="name"
:value="endAreaIndex"
@change="handleAreaChange"
class="uni-input-border"
:disabled="!endCenter"
>
<view class="picker-input">
<text :class="endArea ? 'picker-value' : 'picker-placeholder'">
{{ endArea || "请选择终点区域" }}
</text>
</view>
</picker>
<view v-else class="empty-tip-picker">
<text class="empty-text">暂无数据</text>
</view>
</uni-forms-item>
<!-- 叫料模式下的终点扫码 -->
<uni-forms-item label="终点区域:" label-width="150px" v-else>
<template #label>
<view class="required-label">
<text class="required-mark">*</text>
<text>终点区域:</text>
</view>
</template>
<input
type="text"
v-model="therrInput"
@input="therrInputBlur"
placeholder="请扫描区域码"
class="uni-input-border"
confirm-type="done"
:focus="endInputFocus"
:key="'end-' + endInputFocus"
/>
</uni-forms-item>
</uni-forms>
<view class="footer-action">
<button
@click="handleSubmit"
:disabled="canSubmit"
class="submit-btn"
:class="{ 'btn-disabled': canSubmit }"
>
配送
</button>
</view>
</ifrm>
</template>
<script>
import ifrm from "@/pages/index/ifrm";
import inputBlur from '@/mixin/inputBlur.js'; // 引入 mixin
export default {
mixins: [inputBlur], // 注册 mixin
components: {
ifrm,
},
data() {
return {
// 移除 boxCode, startCode, endStationCode,改用 mixin 的 oneInput, twoInput, therrInput
boxData: null,
startData: null,
showDetails: false,
// 焦点控制变量
boxInputFocus: true,
startInputFocus: false,
endInputFocus: false, // 新增:终点输入框焦点
mockBoxes: {},
workCenters: [],
yieldOrderList: [],
scanMode: "bind",
hasData: false,
workCenters: [],
endAreas: [],
endCenter: "",
endArea: "",
endCenterIndex: 0,
endAreaIndex: 0,
startAreas: [],
startStations: [],
startArea: "蓝色周转盒放置区",
startStation: "",
startAreaIndex: 0,
startStationIndex: 0,
// endStationCode 移除,改用 therrInput
};
},
computed: {
canSubmit() {
if (this.scanMode == "bind") {
return !this.startData || !this.endCenter;
}
if (this.scanMode == "unbind") {
// 叫料模式:需要起点站点和终点扫码值
return !this.startStation || !this.therrInput;
}
return true;
},
},
mounted() {
this.getWorkCenterList();
},
onNavigationBarButtonTap(btn) {
this.$refs.ifrm.topMenuClick(btn);
},
methods: {
// 【核心】实现 mixin 回调:处理物料箱扫码
oneInputData(val) {
if (!val) return;
const code = val.trim();
this.handleBoxConfirmLogic(code);
},
// 【核心】实现 mixin 回调:处理起点站点扫码
twoInputData(val) {
if (!val) return;
const code = val.trim();
this.handleStartConfirmLogic(code);
},
// 【核心】实现 mixin 回调:处理终点区域扫码 (叫料模式)
therrInputData(val) {
if (!val) return;
// 这里可以直接赋值给一个临时变量,或者在提交时直接使用 therrInput
// 为了保持逻辑一致,我们可以在这里做简单的校验或提示
console.log("终点区域扫码结果:", val);
},
// 原 handleBoxConfirm 逻辑
handleBoxConfirmLogic(code) {
if (!code) return;
this.$u.api
.getQuantityLocation({ boxBarcode: code })
.then((res) => {
this.boxData = res.data;
uni.showToast({ title: '物料箱识别成功', icon: 'none' });
// 焦点跳转
this.boxInputFocus = false;
if (this.scanMode === 'bind') {
this.startInputFocus = true;
} else {
// 叫料模式下,扫完箱子可能需要选起点,这里可以根据需求调整
// 假设叫料模式扫完箱子后,焦点保持在箱子或跳到起点Picker(无法自动聚焦picker,故保持当前或提示)
this.boxInputFocus = true;
}
})
.catch(err => {
uni.showToast({ title: err || '查询失败', icon: 'none' });
this.clearOneInput();
this.boxInputFocus = false;
this.$nextTick(() => { this.boxInputFocus = true; });
});
},
// 原 handleStartConfirm 逻辑
handleStartConfirmLogic(code) {
if (!code) return;
this.$u.api
.getStationName({ stationCode: code })
.then((res) => {
this.startData = res.data;
uni.showToast({ title: '起点站点确认', icon: 'none' });
// 焦点跳转:送料模式下,扫完起点,可能需要选终点(Picker无法聚焦),故失焦
this.startInputFocus = false;
})
.catch(err => {
uni.showToast({ title: err || '站点查询失败', icon: 'none' });
this.clearTwoInput();
this.startInputFocus = false;
this.$nextTick(() => { this.startInputFocus = true; });
});
},
// 起点区域选择变化
handleStartAreaChange(e) {
const index = e.detail.value;
this.startArea = this.startAreas[index].name;
this.startAreaIndex = index;
this.startStation = "";
this.startStationIndex = 0;
this.getStartStationsList(this.startAreas[index].id);
},
// 起点站点选择变化
handleStartStationChange(e) {
const index = e.detail.value;
this.startStation = this.startStations[index].name;
this.startStationIndex = index;
},
// 获取起点区域列表
getStartAreasList() {
uni.showLoading({ title: "加载中...", mask: true });
this.$u.api
.getStationRegion()
.then((res) => {
uni.hideLoading();
const list = res.data || [];
this.startAreas = list.map((area) => ({
id: area.stationRegion,
name: area.stationRegion,
stationCodeList: area.stationCodeList || [],
}));
const targetRegion = this.startAreas.find(
(item) => item.name == "蓝色周转盒放置区"
);
if (targetRegion) {
const targetIndex = this.startAreas.findIndex(
(item) => item.id === targetRegion.id
);
if (targetIndex !== -1) {
this.startArea = targetRegion.name;
this.startAreaIndex = targetRegion.id;
const stationCodeList = targetRegion.stationCodeList || [];
if (stationCodeList && stationCodeList.length > 0) {
this.startStations = stationCodeList.map((code) => ({
id: code,
name: code,
}));
this.startStation = stationCodeList[0];
this.startStationIndex = 0;
}
}
} else {
this.startArea = "蓝色周转盒放置区";
this.startAreaIndex = "蓝色周转盒放置区";
this.startStation = null;
}
})
.catch((err) => {
uni.hideLoading();
uni.showToast({ title: "加载区域列表失败", icon: "none" });
});
},
getStartStationsList(areaId) {
if (!areaId) {
this.startStations = [];
return;
}
const area = this.startAreas.find((item) => item.id === areaId);
if (area && area.stationCodeList && area.stationCodeList.length > 0) {
this.startStations = area.stationCodeList.map((code, index) => ({
id: code,
name: code,
}));
if (this.startStations.length > 0) {
this.startStation = this.startStations[0].name;
this.startStationIndex = 0;
}
} else {
this.startStations = [];
this.startStation = "";
this.startStationIndex = 0;
}
},
handleEndChange(e) {
const index = e.detail.value;
const selectedWorkCenter = this.workCenters[index];
this.endCenter = selectedWorkCenter.name;
this.endCenterIndex = selectedWorkCenter.id;
this.endArea = "";
this.endAreaIndex = 0;
if (
selectedWorkCenter.stationRegionList &&
selectedWorkCenter.stationRegionList.length > 0
) {
const firstItem = selectedWorkCenter.stationRegionList[0];
if (typeof firstItem === "object") {
this.endAreas = selectedWorkCenter.stationRegionList.map(
(region) => ({
id: region.id,
name: region.regionName || region.name || region.areaName,
})
);
} else {
this.endAreas = selectedWorkCenter.stationRegionList.map(
(region, idx) => ({
id: idx,
name: region,
})
);
}
if (this.endAreas.length > 0) {
this.endArea = this.endAreas[0].name;
this.endAreaIndex = this.endAreas[0].id;
}
} else {
this.endAreas = [];
}
},
handleAreaChange(e) {
const index = e.detail.value;
this.endArea = this.endAreas[index].name;
this.endAreaIndex = index;
},
handleModeChange(mode) {
this.scanMode = mode;
this.hasData = false;
// 使用 mixin 的清空方法
this.clearInput();
this.boxData = null;
this.startData = null;
this.boxInputFocus = true;
this.startInputFocus = false;
this.endInputFocus = false;
this.startArea = "";
this.startStation = "";
this.startAreaIndex = 0;
this.startStationIndex = 0;
this.startAreas = [];
this.startStations = [];
this.endCenter = "";
this.endArea = "";
this.endCenterIndex = 0;
this.endAreaIndex = 0;
this.endAreas = [];
if (mode === "unbind") {
this.getStartAreasList();
}
uni.showToast({
title: `已切换到${mode === "bind" ? "送料" : "叫料"}模式`,
icon: "none",
});
},
detailsFn() {
this.showDetails = true;
this.$u.api
.boxbarcodeDetails({ boxBarcode: this.boxData.boxBarcode, isDetail: false })
.then((res) => {
this.yieldOrderList = res.data.yieldOrderList;
});
},
getWorkCenterList() {
this.$u.api.getWorkCenter().then((res) => {
this.workCenters = res.data.map((c) => ({
id: c.id,
name: c.wcName,
stationRegionList: c.stationRegionList,
}));
});
},
handleSubmit() {
uni.showLoading({ title: "提交中..." });
let query_ = {
boxBarcode: this.oneInput || (this.boxData ? this.boxData.boxBarcode : ""),
};
if (this.scanMode === "bind") {
if (!this.startData) {
uni.hideLoading();
uni.showToast({ title: '请扫描起点站点', icon: 'none' });
return;
}
query_.startStationCode = this.startData.stationCode;
query_.endWcId = this.endCenterIndex;
query_.stationRegion = this.endArea;
} else if (this.scanMode === "unbind") {
if (!this.startStation) {
uni.hideLoading();
uni.showToast({ title: '请选择起点站点', icon: 'none' });
return;
}
if (!this.therrInput) {
uni.hideLoading();
uni.showToast({ title: '请扫描终点区域', icon: 'none' });
return;
}
query_.startStationCode = this.startStation;
query_.endStationCode = this.therrInput; // 使用 mixin 变量
}
this.$u.api
.boxBindingTesk(query_)
.then((res) => {
uni.showToast({
title: `成功配送!`,
icon: "none",
});
uni.hideLoading();
// 提交成功后清空并重置焦点
this.clearInput();
this.boxData = null;
this.startData = null;
this.endCenter = "";
this.endArea = "";
this.boxInputFocus = true;
this.startInputFocus = false;
this.endInputFocus = false;
})
.catch((err) => {
uni.hideLoading();
uni.showToast({ title: err || '提交失败', icon: 'none' });
});
},
},
};
</script>
<style scoped>
.page-container {
display: flex;
flex-direction: column;
height: 100vh;
background-color: #ffffff;
user-select: none;
font-size: 26rpx;
position: relative;
}
.header {
display: flex;
align-items: center;
justify-content: center;
padding: 6rpx 16rpx;
background-color: #1d4ed8;
color: #ffffff;
flex-shrink: 0;
z-index: 20;
}
.header-title {
font-weight: bold;
font-size: 28rpx;
letter-spacing: 1rpx;
}
.content-scroll {
flex: 1;
overflow-y: auto;
display: flex;
flex-direction: column;
background-color: #ffffff;
}
.module {
padding: 16rpx;
border-bottom: 4rpx solid #e2e8f0;
}
.module-header {
display: flex;
align-items: center;
color: #1e293b;
font-weight: bold;
margin-bottom: 8rpx;
}
.module-icon {
font-size: 32rpx;
margin-right: 8rpx;
}
.module-title {
font-size: 26rpx;
}
.scan-input {
width: 100%;
padding: 16rpx;
background-color: #ffffff;
border: 4rpx solid #94a3b8;
border-radius: 4rpx;
font-size: 28rpx;
font-weight: bold;
color: #0f172a;
}
.scan-input::placeholder {
color: #94a3b8;
font-weight: normal;
}
.box-info {
margin-top: 12rpx;
display: flex;
align-items: center;
justify-content: space-between;
background-color: #f8fafc;
padding: 12rpx;
border-radius: 4rpx;
border: 2rpx solid #e2e8f0;
}
.info-section {
display: flex;
flex-direction: column;
gap: 4rpx;
flex: 1;
}
.info-text {
color: #475569;
font-size: 26rpx;
}
.info-value {
font-weight: bold;
color: #0f172a;
margin-left: 4rpx;
}
.info-value-center {
font-weight: bold;
color: #1d4ed8;
margin-left: 4rpx;
}
.details-btn {
color: #1d4ed8;
background-color: #dbeafe;
font-weight: bold;
border: 2rpx solid #bfdbfe;
font-size: 26rpx;
display: flex;
align-items: center;
}
.details-btn:active {
background-color: #bfdbfe;
}
.arrow {
margin-left: 4rpx;
font-size: 24rpx;
}
.start-confirm {
margin-top: 8rpx;
display: flex;
align-items: center;
font-weight: bold;
color: #059669;
}
.check-icon {
font-size: 28rpx;
margin-right: 8rpx;
}
.confirm-text {
font-size: 26rpx;
}
.picker-input {
width: 100%;
padding: 16rpx;
}
.picker-disabled {
opacity: 0.5;
}
.picker-value {
font-size: 28rpx;
font-weight: bold;
color: #0f172a;
}
.picker-placeholder {
font-size: 28rpx;
color: #94a3b8;
}
.footer {
padding: 16rpx;
background-color: #f1f5f9;
border-top: 2rpx solid #cbd5e1;
flex-shrink: 0;
}
.submit-btn {
width: 100%;
background-color: #1d4ed8;
color: #ffffff;
padding: 20rpx;
border-radius: 4rpx;
font-weight: bold;
font-size: 30rpx;
border: none;
}
.submit-btn:active {
background-color: #1e40af;
}
.btn-disabled {
opacity: 0.5;
background-color: rgba(29, 78, 216, 0.5);
}
/* Popup Styles */
.popup-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 100;
display: flex;
align-items: flex-end;
}
.popup-content {
background-color: #ffffff;
border-radius: 16rpx 16rpx 0 0;
max-height: 70vh;
width: 100%;
display: flex;
flex-direction: column;
border-top: 4rpx solid #94a3b8;
}
.popup-header {
padding: 24rpx;
background-color: #ffffff;
border-bottom: 4rpx solid #e2e8f0;
flex-shrink: 0;
text-align: center;
}
.popup-title {
font-weight: bold;
font-size: 30rpx;
color: #0f172a;
text-align: center;
}
.popup-scroll {
flex: 1;
padding: 24rpx;
overflow-y: auto;
width: auto;
}
.popup-orders {
display: flex;
flex-direction: column;
gap: 16rpx;
}
.popup-order-item {
background-color: #f8fafc;
padding: 16rpx;
border: 2rpx solid #cbd5e1;
border-radius: 4rpx;
}
.popup-order-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8rpx;
}
.popup-order-no {
font-weight: bold;
color: #0f172a;
font-size: 26rpx;
}
.popup-order-qty {
color: #1d4ed8;
font-weight: bold;
font-size: 26rpx;
}
.popup-order-name {
color: #475569;
font-size: 24rpx;
}
.popup-footer {
padding: 16rpx;
background-color: #f1f5f9;
border-top: 2rpx solid #cbd5e1;
flex-shrink: 0;
}
.popup-close-btn {
width: 100%;
background-color: #ffffff;
border: 4rpx solid #94a3b8;
color: #1e293b;
padding: 16rpx;
border-radius: 4rpx;
font-weight: bold;
font-size: 28rpx;
}
.popup-close-btn:active {
background-color: #e2e8f0;
}
.help-tip {
position: absolute;
top: 45%;
left: 0;
width: 100%;
display: flex;
justify-content: center;
pointer-events: none;
opacity: 0.5;
z-index: 10;
}
.tip-content {
background-color: #1e293b;
color: #ffffff;
font-size: 20rpx;
padding: 8rpx 16rpx;
border-radius: 4rpx;
display: flex;
align-items: center;
}
.tip-icon {
margin-right: 8rpx;
font-size: 24rpx;
}
.tip-text {
font-size: 20rpx;
}
.bottom-input-row {
display: flex;
gap: 10px;
margin-bottom: 12px;
}
.weight-input-wrapper {
flex: 1;
}
.submit-btn {
width: 88px;
height: 50px;
background-color: #155dfc;
border-radius: 10px;
color: #ffffff;
font-size: 16px;
font-weight: 500;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.1),
0px 1px 2px 0px rgba(0, 0, 0, 0.1);
border: none;
padding: 0;
line-height: 50px;
}
.submit-btn.disabled {
opacity: 0.5;
}
.mode-switch {
display: flex;
width: 140px;
height: 34px;
border-radius: 4px;
border: 1px solid rgba(21, 93, 252, 1);
overflow: hidden;
margin-left: auto;
}
.switch-btn {
flex: 1;
text-align: center;
line-height: 32px;
font-size: 14px;
color: rgba(21, 93, 252, 1);
background-color: #fff;
transition: all 0.2s;
}
.switch-btn.active {
background-color: rgba(21, 93, 252, 1);
color: #fff;
}
.label-left {
flex-direction: row !important;
margin-top: 20rpx;
}
/* 底部固定按钮区域 */
.footer-action {
position: fixed;
left: 0;
right: 0;
bottom: 0;
padding: 20rpx 32rpx;
background-color: #ffffff;
border-top: 2rpx solid #e2e8f0;
box-shadow: 0 -2rpx 8rpx rgba(0, 0, 0, 0.05);
z-index: 50;
}
.picker-input {
width: 100%;
padding: 16rpx;
}
.picker-value {
font-size: 28rpx;
font-weight: bold;
color: #0f172a;
}
.picker-placeholder {
font-size: 28rpx;
color: #94a3b8;
}
/* 禁用状态 */
.picker-input.disabled {
opacity: 0.5;
background-color: #f1f5f9;
}
.uni-input-border {
margin-bottom: 20rpx;
}
.required-label {
display: flex;
align-items: center;
}
.required-mark {
color: #ff0000;
font-size: 28rpx;
font-weight: bold;
margin-right: 4rpx;
}
/* 空数据提示样式 */
.empty-tip-picker {
width: 100%;
padding: 16rpx;
background-color: #f8fafc;
border: 2rpx solid #e2e8f0;
border-radius: 4rpx;
margin-bottom: 20rpx;
}
.empty-text {
color: #94a3b8;
font-size: 28rpx;
text-align: center;
display: block;
}
.uni-input-border {
margin-bottom: 20rpx;
}
</style>