pages/sockroom/sockroom.vue
@@ -11,12 +11,8 @@
                     <input 
                     id="pdacode"
                     type="text"
                     ref="barcodeInput"
                     v-model="barcode"
                     placeholder="请扫码(12位以下为托盘码)"
                     :focus="focusState"
                     @focus="focusFn"
                     @blur="focusState = false"
                     placeholder="请扫码"
                     />
                  </view>
               </view>
@@ -40,6 +36,25 @@
            </view>
         </view>
         
            <view class="form-card">
               <view class="form-row">
                  <view class="label">
                     <text class="required">*</text>物料码:
                  </view>
                  <view class="picker-wrap" hover-class="picker-hover">
                     <view class="textareainput">
                        <textarea
                        style="max-height: 15px;overflow: hidden;"
                        id="pdacode"
                        type="text"
                        v-model="thingCode"
                        placeholder="请扫码"
                        @input="handleBarcodeInput"
                        />
                     </view>
                  </view>
               </view>
            </view>
         <!-- 物料信息输入栏(对应后端MatList) -->
         <view class="form-card material-card">
            <view class="card-title">物料信息</view>
@@ -118,334 +133,287 @@
               </view>
            </view>
         </view>
         <!-- 操作按钮组 -->
         <view class="btn-group">
            <button class="operate-btn scan-btn" @click="startScan">开始扫码</button>
            <button class="operate-btn submit-btn" @click="submit">成品入库</button>
         </view>
      </view>
   </view>
</template>
<script>
import { onLoad } from '../../uni_modules/uview-ui/libs/mixin/mixin'
export default {
   onLoad(){
      // 先读取缓存,再执行请求
      this.baseUrl = uni.getStorageSync('baseUrl');
      this.token = uni.getStorageSync('token');
      // 页面加载时 mode 为空,调用接口后才填充数据
   onLoad(options){
      this.checkLoginStatus();
      this.POSTinfo(); 
   },
   mounted() {
      this.setupScanListener();
   onShow() {
      this.checkLoginStatus();
   },
   beforeDestroy() {
      this.removeScanListener();
   onHide() {
      if (this.scanTimeout) {
         clearTimeout(this.scanTimeout);
         this.scanTimeout = null;
      }
   },
   data() {
      return {
         thingCode:'',
         focusState: false,
         baseUrl: '',
         barcodeNum:'',
         barcode: '', // 托盘码
         palletizingNo: '1', // 码垛位编号
         boxType: '', // 木箱类型
         token: '',
         barcode: '',
         palletizingNo: '1',
         boxType: '',
         matList: {
            matnr: '', // 规格
            maktx: '', // 物料名称
            batch: '', // 木箱编号
            model: '', // 卷编号
            position: '1', // 木箱码垛位置
            weight: '', // 净重
            roughWeight: '', // 毛重
            anfme: 1.0, // 默认值1.0
            rollExtent: '', // 卷长度
            joint: 0, // 接头
            matnr: '',
            maktx: '',
            batch: '',
            model: '',
            position: '1',
            weight: '',
            roughWeight: '',
            anfme: 1.0,
            rollExtent: '',
            joint: 0,
            cutting: 2,
            qualified: 0,
            modelFront: '', // 分切前箱编号
            batchFront: '' ,// 分切前卷号
            modelFront: '',
            batchFront: ''
         },
         // 初始化 mode 为空数组 → 页面加载时箱型无数据
         mode: [], 
         index_mode: 0,
         index_num: 0,
         submitData: {} ,// 最终提交给后端的数据
         scanBuffer: '', // 添加扫码缓冲区
         scanTimeout: null, // 扫码超时定时器
         isScanning: false, // 扫码状态标志
         scanMode: 'tray', // 扫码模式: tray-托盘码, material-物料码
         lastFillTarget: null // 记录上次填充的目标
         submitData: {},
         scanBuffer: '',
         scanTimeout: null,
         lastScanTime: 0,
         isProcessingScan: false
      }
   },
   methods: {
      POSTinfo(){
         uni.request({
            url: this.baseUrl + '/mobile/box/type/complete/auth/v2',
            method:'POST',
            header: {
               'Content-Type': 'application/json'
            },
            success:(res)=>{
               // 接口返回后才填充箱型数据
               this.mode = res.data.data.map(item => item.boxSpecs || '').filter(item => item);
               console.log('提取的箱型数据:', this.mode);
            },
            fail:(err)=>{
               console.error('获取箱型失败:', err)
            }
         })
      },
      // 设置扫码监听
      setupScanListener() {
         // 移除之前的监听
         this.removeScanListener();
      // 检查登录状态
      checkLoginStatus() {
         this.token = uni.getStorageSync('token');
         this.baseUrl = uni.getStorageSync('baseUrl');
         
         // 监听键盘输入事件
         document.addEventListener('keydown', this.handleKeyDown);
      },
      // 移除监听
      removeScanListener() {
         document.removeEventListener('keydown', this.handleKeyDown);
         if (this.scanTimeout) {
            clearTimeout(this.scanTimeout);
         }
      },
      // 键盘事件处理
      handleKeyDown(e) {
         // 排除功能键(F1-F12等)
         if (e.key.length > 1 && !['Enter', 'Tab'].includes(e.key)) {
            return;
         }
         // PDA扫码通常以回车结束
         if (e.key === 'Enter') {
            e.preventDefault(); // 阻止默认行为
            this.processScanCode();
         } else {
            // 累积扫码字符(排除Shift等修饰键)
            if (!e.ctrlKey && !e.altKey && !e.metaKey && e.key.length === 1) {
               this.scanBuffer += e.key;
               // 重置超时定时器
               if (this.scanTimeout) {
                  clearTimeout(this.scanTimeout);
               }
               // 设置超时(假设扫码间隔超过200ms表示一次扫码完成)
               this.scanTimeout = setTimeout(() => {
                  this.processScanCode();
               }, 200);
            }
         }
      },
      // 处理扫码结果
      processScanCode() {
         if (!this.scanBuffer) return;
         const scanResult = this.scanBuffer.trim();
         this.scanBuffer = '';
         // 判断扫码长度决定填充位置
         if (scanResult.length <= 12) {
            // 12位以下填充到托盘编码
            this.barcode = scanResult;
            this.lastFillTarget = 'tray';
            uni.showToast({
               title: '托盘码已填充',
               icon: 'success',
               duration: 1000
            });
         } else {
            // 12位以上填充到物料信息
            this.autoFillMaterialInfo(scanResult);
            this.lastFillTarget = 'material';
            uni.showToast({
               title: '物料码已填充',
               icon: 'success',
               duration: 1000
            });
         }
         // 清除定时器
         if (this.scanTimeout) {
            clearTimeout(this.scanTimeout);
            this.scanTimeout = null;
         }
         // 自动失焦
         setTimeout(() => {
            this.focusState = false;
         }, 100);
      },
      // 自动填充物料信息
      autoFillMaterialInfo(code) {
         // 尝试解析二维码内容
         if (code.includes('卷号:') && code.includes('箱号:')) {
            // 如果是带格式的二维码
            const parseResult = this.parseQrCodeText(code);
            this.matList = { ...this.matList, ...parseResult };
            console.log('解析后的物料信息:', parseResult);
         } else {
            // 如果是纯数字/条码,智能填充
            this.smartFillMaterialFields(code);
         }
      },
      // 智能填充物料字段
      smartFillMaterialFields(code) {
         // 根据业务逻辑判断,这里假设长条码可能是卷号或箱号
         // 如果卷号为空,优先填充卷号
         if (!this.matList.model) {
            this.matList.model = code;
         }
         // 如果卷号已有值但箱号为空,填充箱号
         else if (!this.matList.batch) {
            this.matList.batch = code;
         }
         // 如果两者都有值,提示用户
         else {
         if (!this.token || !this.baseUrl) {
            uni.showModal({
               title: '提示',
               content: `扫描到长条码: ${code}\n请选择填充位置:`,
               showCancel: true,
               cancelText: '卷号',
               confirmText: '箱号',
               content: '请先登录',
               showCancel: false,
               success: (res) => {
                  if (res.confirm) {
                     this.matList.batch = code;
                  } else if (res.cancel) {
                     this.matList.model = code;
                     uni.navigateTo({
                        url: '/pages/login/login'
                     });
                  }
               }
            });
            return false;
         }
         return true;
      },
      
      // 解析二维码文本(用于扫货物码功能)
      parseQrCodeText(qrText) {
         const result = {};
         const lines = qrText.split('\n');
      // 获取箱型数据
      POSTinfo(){
         if (!this.checkLoginStatus()) return;
         
         lines.forEach(line => {
            if (line.includes('卷号:')) {
               result.model = line.split('卷号:')[1]?.trim() || '';
            }
            if (line.includes('箱号:')) {
               result.batch = line.split('箱号:')[1]?.trim() || '';
            }
            if (line.includes('规格:')) {
               result.matnr = line.split('规格:')[1]?.trim() || '';
            }
            // 可以根据需要添加更多字段的解析
         uni.showLoading({
            title: '加载中...'
         });
         
         return result;
      },
      // 手动开始扫码
      startScan() {
         // 清空当前值并聚焦
         this.scanBuffer = '';
         this.$nextTick(() => {
            this.focusState = true;
            // 延迟确保input已聚焦
            setTimeout(() => {
               const input = document.getElementById('pdacode');
               if (input) {
                  input.focus();
                  input.select();
               }
            }, 100);
         });
      },
      // 修改focusFn
      focusFn() {
         this.focusState = true;
         this.scanBuffer = ''; // 聚焦时清空缓冲区
      },
      // 保留原有的扫货物码功能
      scantwo() {
         uni.scanCode({
            autoDecodeCharSet: true,
            scanType: ['qrCode', 'barCode'],
         uni.request({
            url: this.baseUrl + '/mobile/box/type/complete/auth/v2',
            method: 'POST',
            header: {
               'Content-Type': 'application/json',
               'Authorization': 'Bearer ' + this.token
            },
            success: (res) => {
               const qrResult = res.result.trim();
               this.autoFillMaterialInfo(qrResult);
               uni.hideLoading();
               console.log('箱型接口返回:', res.data);
               
               uni.showToast({
                  title: '物料信息扫码成功',
                  icon: 'success',
                  duration: 2000
               });
               if (res.data && res.data.code === 200) {
                  if (Array.isArray(res.data.data)) {
                     // 尝试多种可能的字段名
                     this.mode = res.data.data
                        .map(item => item.boxSpecs || item.boxType || item.typeName || item.name || '')
                        .filter(item => item && item.trim() !== '');
                     console.log('提取的箱型列表:', this.mode);
                     if (this.mode.length === 0) {
                        uni.showToast({
                           title: '无可用箱型',
                           icon: 'none'
                        });
                     }
                  } else {
                     console.warn('返回数据格式不符,不是数组:', res.data.data);
                     uni.showToast({
                        title: '数据格式错误',
                        icon: 'none'
                     });
                  }
               } else {
                  uni.showToast({
                     title: res.data?.message || '获取箱型失败',
                     icon: 'none'
                  });
               }
            },
            fail: (err) => {
               console.log('扫码失败:', err);
               uni.hideLoading();
               console.error('获取箱型失败:', err);
               uni.showToast({
                  title: '扫码失败或取消',
                  title: '网络错误',
                  icon: 'none'
               });
            }
         });
      },
      modePickerChange(e) {
         this.index_mode = e.detail.value;
         this.boxType = this.mode[this.index_mode]; // 同步到boxType
         this.boxType = this.mode[this.index_mode];
         console.log('选择的箱型:', this.boxType);
      },
      
      numPickerChange(e) {
         this.index_num = e.detail.value;
      },
      // 修改重置方法
      resetForm() {
         this.barcodeNum = '';
         this.barcode = '';
         this.boxType = '';
         this.index_mode = 0;
         this.index_num = 0;
         this.submitData = {};
         this.mode = [];
         this.lastFillTarget = null;
         this.scanBuffer = '';
      handleBarcodeInput(e) {
            // 在uni-app中,textarea的input事件可能是e.detail.value
            const value = e.detail ? e.detail.value : e.target.value;
            this.thingCode = value;
            this.parseBarcodeContent(value);
         },
         
         // 重置物料信息
         parseBarcodeContent(content) {
            if (!content) return;
            // 按行分割内容
            const lines = content.split('\n').filter(line => line.trim() !== '');
            console.log('解析的行数据:', lines);
            // 根据你提供的样例格式解析
            // 格式为:
            // 2511080305A22B6  (卷号)
            // Y251113764        (箱号)
            // 5μm×1050mm       (规格)
            // 8700 m           (长度)
            // 418.0 kg         (净重)
            // 441.0 kg         (毛重)
            // 0 个             (接头)
            if (lines.length >= 7) {
               // 卷号 (第一行)
               this.matList.model = lines[0].trim();
               // 箱号 (第二行)
               this.matList.batch = lines[1].trim();
               // 规格 (第三行)
               this.matList.matnr = lines[2].trim();
               // 长度 (第四行) - 提取数字部分
               const lengthMatch = lines[3].match(/([\d.]+)/);
               if (lengthMatch) {
                  this.matList.rollExtent = lengthMatch[1];
               }
               // 净重 (第五行) - 提取数字部分
               const weightMatch = lines[4].match(/([\d.]+)/);
               if (weightMatch) {
                  this.matList.weight = weightMatch[1];
               }
               // 毛重 (第六行) - 提取数字部分
               const roughWeightMatch = lines[5].match(/([\d.]+)/);
               if (roughWeightMatch) {
                  this.matList.roughWeight = roughWeightMatch[1];
               }
               // 接头 (第七行) - 提取数字部分
               const jointMatch = lines[6].match(/([\d.]+)/);
               if (jointMatch) {
                  this.matList.joint = jointMatch[1];
               }
               console.log('解析后的数据:', this.matList);
            } else if (lines.length > 0) {
               // 如果行数不够,也可以尝试智能匹配
               this.autoMatchFields(lines);
            }
         },
         // 智能匹配方法,用于处理可能的不规则数据
         autoMatchFields(lines) {
            lines.forEach((line, index) => {
               line = line.trim();
               // 根据内容特征匹配
               if (index === 0 && !this.matList.model) {
                  // 假设第一行总是卷号
                  this.matList.model = line;
               } else if (index === 1 && !this.matList.batch) {
                  // 假设第二行总是箱号
                  this.matList.batch = line;
               } else if (line.includes('μm') || line.includes('mm') || line.includes('×')) {
                  // 包含规格特征的
                  this.matList.matnr = line;
               } else if (line.includes('m') && line.match(/[\d.]+ m/)) {
                  // 长度特征
                  const match = line.match(/([\d.]+)/);
                  if (match) this.matList.rollExtent = match[1];
               } else if (line.includes('kg') && line.match(/[\d.]+ kg/)) {
                  // 重量特征,需要区分净重和毛重
                  const match = line.match(/([\d.]+)/);
                  if (match) {
                     const weightValue = match[1];
                     if (!this.matList.weight) {
                        this.matList.weight = weightValue;
                     } else if (!this.matList.roughWeight) {
                        this.matList.roughWeight = weightValue;
                     }
                  }
               } else if (line.includes('个') && line.match(/[\d.]+ 个/)) {
                  // 接头特征
                  const match = line.match(/([\d.]+)/);
                  if (match) this.matList.joint = match[1];
               }
            });
         },
      // 重置表单
      resetForm() {
         this.barcode = '';
         this.boxType = '';
         this.index_mode = 0;
         this.matList = {
            matnr: '',
            maktx: '',
            batch: '',
            model: '',
            position: '1',
            position: '1',
            weight: '',
            roughWeight: '',
            anfme: 1.0,
            anfme: 1.0,
            rollExtent: '',
            joint: 0,
            cutting: 2,
            qualified: 0,
            joint: 0,
            cutting: 2,
            qualified: 0,
            modelFront: '',
            batchFront: ''
         };
         this.scanBuffer = '';
         this.focusState = false;
      },
      
      // 提交数据
      submit() {
         // 校验必填项
         // 1. 验证登录
         if (!this.checkLoginStatus()) return;
         // 2. 验证必填项
         if (!this.barcode) {
            uni.showToast({title: '请扫描托盘编码', icon: 'none'});
            return;
@@ -456,55 +424,79 @@
            return;
         }
         
         if (!this.matList.model || !this.matList.batch) {
            uni.showToast({title: '请填写卷号和箱号', icon: 'none'});
         if (!this.matList.model) {
            uni.showToast({title: '请填写卷号', icon: 'none'});
            return;
         }
         
         // 数据类型转换
         if (!this.matList.batch) {
            uni.showToast({title: '请填写箱号', icon: 'none'});
            return;
         }
         // 3. 准备数据
         const matItem = {
            ...this.matList,
            weight: this.matList.weight ? Number(this.matList.weight) : null,
            roughWeight: this.matList.roughWeight ? Number(this.matList.roughWeight) : null,
            rollExtent: this.matList.rollExtent ? Number(this.matList.rollExtent) : null,
            joint: this.matList.joint ? Number(this.matList.joint) : 0,
            anfme: 1.0,
            cutting: 2,
            qualified: 0
            joint: this.matList.joint ? Number(this.matList.joint) : 0
         };
         
         // 组装提交数据
         this.submitData = {
         const submitData = {
            barcode: this.barcode,
            palletizingNo: this.palletizingNo,
            boxType: this.boxType,
            matList: [matItem]
         };
         
         console.log('提交数据:', this.submitData);
         console.log('提交数据:', submitData);
         
         // 提交请求
         // 4. 发送请求
         uni.showLoading({
            title: '提交中...',
            mask: true
         });
         uni.request({
            url: this.baseUrl + '/mobile/truss/comd/auth/v2',
            method: 'POST',
            header: {'Content-Type': 'application/json'},
            data: this.submitData,
            header: {
               'Content-Type': 'application/json',
               'Authorization': 'Bearer ' + this.token
            },
            data: submitData,
            success: (res) => {
               console.log('后端返回:', res);
               uni.hideLoading();
               console.log('提交返回:', res.data);
               
               if (res.data && res.data.code === 200) {
                  uni.showToast({title: '提交成功', icon: 'success'});
                  this.resetForm();
               if (res.data.code === 200) {
                  uni.showToast({
                     title: '提交成功',
                     icon: 'success',
                     duration: 2000
                  });
                  setTimeout(() => {
                     this.resetForm();
                  }, 1500);
               } else {
                  uni.showToast({title: res.data.message || '提交失败', icon: 'none'});
                  uni.showToast({
                     title: res.data.message || '提交失败',
                     icon: 'none'
                  });
               }
            },
            fail: (err) => {
               console.error('请求失败详情:', err);
               uni.showToast({title: '网络请求失败', icon: 'none'});
               uni.hideLoading();
               console.error('提交失败:', err);
               uni.showToast({
                  title: '网络错误',
                  icon: 'none'
               });
            }
         });
      }
      },
   }
}
</script>
@@ -532,12 +524,19 @@
   padding: 24rpx;
   transition: all 0.2s ease;
}
.textareainput{
   width: 60%;
   height: 15px;
   font-size: 28rpx;
   padding: 16rpx 12rpx;
   border: 1rpx solid #e5e7eb;
   border-radius: 8rpx;
   transition: border-color 0.2s ease;
}
/* 物料信息卡片特殊样式 */
.material-card {
   padding: 0;
}
.card-title {
   font-size: 30rpx;
   font-weight: 500;
@@ -631,6 +630,7 @@
/* 物料输入框 */
.material-input {
   width: 45%;
   font-size: 28rpx;
   color: #1f2937;
   padding: 16rpx 12rpx;