lsh
1 天以前 218e046d9339ddf3a729930f6bb7b0b77bbed858
pages/sockroom/sockroom.vue
@@ -1,35 +1,32 @@
<template>
   <view class="sockpage">
      <view class="column">
         <!-- 托盘编码 -->
         <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="picker-wrap">
                  <view class="input-box">
                     <input 
                     id="pdacode"
                     type="text"
                     ref="barcodeInput"
                     v-model="barcode"
                     placeholder="请扫码(12位以下为托盘码)"
                     :focus="focusState"
                     @focus="focusFn"
                     @blur="focusState = false"
                        type="text"
                        v-model="barcode"
                        placeholder="请扫码"
                        placeholder-class="input-placeholder"
                     />
                  </view>
               </view>
            </view>
         </view>
         
         <!-- 箱型栏(对应后端boxType) -->
         <!-- 箱型选择 -->
         <view class="form-card">
            <view class="form-row">
               <view class="label">
                  <text class="required">*</text>箱    型:
                  <text class="required">*</text>箱型:
               </view>
               <view class="picker-wrap" hover-class="picker-hover">
               <view class="picker-wrap">
                  <picker @change="modePickerChange" :value="index_mode" :range="mode">
                     <view class="input-box">
                        <text class="input-placeholder" v-if="!mode.length || !mode[index_mode]">请选择箱型</text>
@@ -40,364 +37,598 @@
            </view>
         </view>
         
         <!-- 物料信息输入栏(对应后端MatList) -->
         <!-- 物料码扫码(隐藏但可用) -->
         <!-- <view class="form-card" style="height: 0; overflow: hidden; padding: 0; margin: 0;"> -->
         <view class="form-card" style=" overflow: hidden; padding: 0; margin: 0;">
            <view class="form-row">
               <view class="label">
                  <text class="required">*</text>物料码:
               </view>
               <view class="picker-wrap">
                  <view class="input-box">
                     <input
                        type="text"
                        v-model="scanBuffer"
                        placeholder="请扫码"
                        @input="handleMaterialScan"
                        ref="scanInput"
                     />
                  </view>
               </view>
            </view>
         </view>
         <!-- 物料信息 -->
         <view class="form-card material-card">
            <view class="card-title">物料信息</view>
            <view class="material-grid">
               <view class="material-item">
                  <text class="material-label"><text class="required">*</text>卷号:</text>
                  <input
                     class="material-input"
                     type="text"
                     v-model="matList.model"
                     placeholder="请扫码或输入卷号"
                     placeholder-class="input-placeholder"
                     ref="modelInput"
                  />
            <!-- 多物料列表 -->
            <view class="material-list" v-if="matList.length > 0">
               <view class="material-item" v-for="(mat, index) in matList" :key="mat.id">
                  <view class="material-header">
                     <text class="material-title">物料 {{ index + 1 }}</text>
                     <view class="material-actions">
                        <view class="action-btn" @click="removeMaterial(index)">删除</view>
                     </view>
                  </view>
                  <view class="material-grid">
                     <view class="material-item">
                        <text class="material-label"><text class="required">*</text>位置:</text>
                        <input
                           class="material-input"
                           type="text"
                           v-model="mat.position"
                           placeholder="请选择位置"
                           placeholder-class="input-placeholder"
                        />
                     </view>
                     <view class="material-item">
                        <text class="material-label"><text class="required">*</text>箱号:</text>
                        <input
                           class="material-input"
                           type="text"
                           v-model="mat.batch"
                           placeholder="请扫码或输入箱号"
                           placeholder-class="input-placeholder"
                        />
                     </view>
                     <view class="material-item">
                        <text class="material-label"><text class="required">*</text>卷号:</text>
                        <input
                           class="material-input"
                           type="text"
                           v-model="mat.model"
                           placeholder="请扫码或输入卷号"
                           placeholder-class="input-placeholder"
                        />
                     </view>
                     <view class="material-item">
                        <text class="material-label">规格:</text>
                        <input
                           class="material-input"
                           type="text"
                           v-model="mat.matnr"
                           placeholder="请输入规格"
                           placeholder-class="input-placeholder"
                        />
                     </view>
                     <view class="material-item">
                        <text class="material-label">长度:</text>
                        <input
                           class="material-input"
                           type="number"
                           v-model="mat.rollExtent"
                           placeholder="请输入长度"
                           placeholder-class="input-placeholder"
                        />
                     </view>
                     <view class="material-item">
                        <text class="material-label">净重:</text>
                        <input
                           class="material-input"
                           type="number"
                           v-model="mat.weight"
                           placeholder="请输入净重"
                           placeholder-class="input-placeholder"
                        />
                     </view>
                     <view class="material-item">
                        <text class="material-label">毛重:</text>
                        <input
                           class="material-input"
                           type="number"
                           v-model="mat.roughWeight"
                           placeholder="请输入毛重"
                           placeholder-class="input-placeholder"
                        />
                     </view>
                     <view class="material-item">
                        <text class="material-label">接头:</text>
                        <input
                           class="material-input"
                           type="number"
                           v-model="mat.joint"
                           placeholder="请输入接头数"
                           placeholder-class="input-placeholder"
                        />
                     </view>
                  </view>
               </view>
               <view class="material-item">
                  <text class="material-label"><text class="required">*</text>箱号:</text>
                  <input
                     class="material-input"
                     type="text"
                     v-model="matList.batch"
                     placeholder="请扫码或输入箱号"
                     placeholder-class="input-placeholder"
                     ref="batchInput"
                  />
               </view>
               <view class="material-item">
                  <text class="material-label">规格:</text>
                  <input
                     class="material-input"
                     type="text"
                     v-model="matList.matnr"
                     placeholder="请输入规格"
                     placeholder-class="input-placeholder"
                  />
               </view>
               <view class="material-item">
                  <text class="material-label">长度:</text>
                  <input
                     class="material-input"
                     type="number"
                     v-model="matList.rollExtent"
                     placeholder="请输入长度"
                     placeholder-class="input-placeholder"
                  />
               </view>
               <view class="material-item">
                  <text class="material-label">净重:</text>
                  <input
                     class="material-input"
                     type="number"
                     v-model="matList.weight"
                     placeholder="请输入净重"
                     placeholder-class="input-placeholder"
                  />
               </view>
               <view class="material-item">
                  <text class="material-label">毛重:</text>
                  <input
                     class="material-input"
                     type="number"
                     v-model="matList.roughWeight"
                     placeholder="请输入毛重"
                     placeholder-class="input-placeholder"
                  />
               </view>
               <view class="material-item">
                  <text class="material-label">接头:</text>
                  <input
                     class="material-input"
                     type="number"
                     v-model="matList.joint"
                     placeholder="请输入接头数"
                     placeholder-class="input-placeholder"
                  />
               </view>
            </view>
            <!-- 空状态提示 -->
            <view class="empty-state" v-else>
               <view class="empty-icon">📦</view>
               <text class="empty-text">暂无物料信息</text>
               <text class="empty-tip">请扫描物料码添加物料</text>
            </view>
            <!-- 扫码按钮 -->
            <view class="scan-btn" @click="focusScanInput">
               <text class="scan-icon">+</text>
               <text class="scan-text">扫码添加物料</text>
            </view>
         </view>
         
         <!-- 操作按钮组 -->
         <view class="btn-group">
            <button class="operate-btn scan-btn" @click="startScan">开始扫码</button>
            <button class="operate-btn submit-btn" @click="submit">成品入库</button>
            <button class="operate-btn submit-btn" @click="submit">组托</button>
         </view>
      </view>
      <!-- 扫码确认弹窗 -->
      <view class="modal-mask" v-if="showScanModal" @click="closeModal">
         <view class="modal-content" @click.stop>
            <view class="modal-header">
               <text class="modal-title">扫码确认</text>
            </view>
            <view class="modal-body">
               <view class="scan-preview">
                  <text class="scan-label">扫描到物料:</text>
                  <text class="scan-code">{{ tempMaterial.model || '未知物料' }}</text>
               </view>
               <view class="preview-grid">
                  <view class="preview-item">
                     <text class="preview-label">箱号:</text>
                     <text class="preview-value">{{ tempMaterial.batch || '未识别' }}</text>
                  </view>
                  <view class="preview-item">
                     <text class="preview-label">卷号:</text>
                     <text class="preview-value">{{ tempMaterial.model || '未识别' }}</text>
                  </view>
                  <view class="preview-item">
                     <text class="preview-label">规格:</text>
                     <text class="preview-value">{{ tempMaterial.matnr || '未识别' }}</text>
                  </view>
                  <view class="preview-item">
                     <text class="preview-label">长度:</text>
                     <text class="preview-value">{{ tempMaterial.rollExtent || '未识别' }}</text>
                  </view>
                  <view class="preview-item">
                     <text class="preview-label">净重:</text>
                     <text class="preview-value">{{ tempMaterial.weight || '未识别' }}</text>
                  </view>
                  <view class="preview-item">
                     <text class="preview-label">毛重:</text>
                     <text class="preview-value">{{ tempMaterial.roughWeight || '未识别' }}</text>
                  </view>
                  <view class="preview-item">
                     <text class="preview-label">接头:</text>
                     <text class="preview-value">{{ tempMaterial.joint || '未识别' }}</text>
                  </view>
                  <view class="preview-item full-width">
                     <text class="preview-label">物料位置:</text>
                     <view class="position-selector" @click.stop="togglePositionPicker">
                        <view class="position-display">
                           <text v-if="positionIndex >= 0">{{ positionOptions[positionIndex].label }}</text>
                           <text v-else class="placeholder">请选择位置</text>
                           <text class="position-arrow">▼</text>
                        </view>
                     </view>
                  </view>
                  <!-- 新增:自定义选择器弹出层 -->
                  <view v-if="showPositionPicker" class="custom-picker-overlay" @click.stop="closePositionPicker">
                     <view class="custom-picker-content" @click.stop>
                        <view class="custom-picker-header">
                           <text class="custom-picker-title">选择物料位置</text>
                           <view class="custom-picker-close" @click="closePositionPicker">×</view>
                        </view>
                        <view class="custom-picker-list">
                           <view
                              v-for="(item, index) in positionOptions"
                              :key="index"
                              class="custom-picker-item"
                              :class="{'selected': positionIndex === index}"
                              @click="selectPosition(index)"
                           >
                              <text class="custom-picker-text">{{ item.label }}</text>
                              <text v-if="positionIndex === index" class="custom-picker-check">✓</text>
                           </view>
                        </view>
                     </view>
                  </view>
               </view>
            </view>
            <view class="modal-footer">
               <button class="modal-btn cancel" @click="cancelAddMaterial">取消</button>
               <button class="modal-btn confirm" @click="confirmAddMaterial">确定添加</button>
            </view>
         </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 为空,调用接口后才填充数据
      this.POSTinfo();
   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;
      }
   },
   onReady() {
      // 在页面准备就绪后初始化扫码监听
      setTimeout(() => {
         this.initScanListener();
      }, 500);
   },
   data() {
      return {
         focusState: false,
         baseUrl: '',
         barcodeNum:'',
         barcode: '', // 托盘码
         palletizingNo: '1', // 码垛位编号
         boxType: '', // 木箱类型
         matList: {
            matnr: '', // 规格
            maktx: '', // 物料名称
            batch: '', // 木箱编号
            model: '', // 卷编号
            position: '1', // 木箱码垛位置
            weight: '', // 净重
            roughWeight: '', // 毛重
            anfme: 1.0, // 默认值1.0
            rollExtent: '', // 卷长度
            joint: 0, // 接头
            cutting: 2,
            qualified: 0,
            modelFront: '', // 分切前箱编号
            batchFront: '' ,// 分切前卷号
         },
         // 初始化 mode 为空数组 → 页面加载时箱型无数据
         mode: [],
         token: '',
         barcode: '',
         boxType: '',
         mode: [],
         index_mode: 0,
         index_num: 0,
         submitData: {} ,// 最终提交给后端的数据
         scanBuffer: '', // 添加扫码缓冲区
         scanTimeout: null, // 扫码超时定时器
         isScanning: false, // 扫码状态标志
         scanMode: 'tray', // 扫码模式: tray-托盘码, material-物料码
         lastFillTarget: null // 记录上次填充的目标
         // 扫码相关
         scanBuffer: '',
         scanTimeout: null,
         // 物料列表
         matList: [],
         materialId: 1,
         // 弹窗相关
         showScanModal: false,
         tempMaterial: {
            model: '',
            batch: '',
            matnr: '',
            rollExtent: '',
            weight: '',
            roughWeight: '',
            joint: 0,
            position: '1' // 新增:物料位置,默认值为"1"(左)
         },
         // 新增:物料位置下拉框相关数据
         positionIndex: 0, // 默认选中第一个(左)
         // 新增:控制自定义选择器显示
         showPositionPicker: false,
         positionOptions: [
            { label: '1', value: '1' },
            { label: '2', value: '2' }
         ]
      }
   },
   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)
      // 新增:切换位置选择器显示
      togglePositionPicker() {
         this.showPositionPicker = !this.showPositionPicker;
      },
      // 新增:关闭位置选择器
      closePositionPicker() {
         this.showPositionPicker = false;
      },
      // 新增:选择位置
      selectPosition(index) {
         this.positionIndex = index;
         this.tempMaterial.position = this.positionOptions[index].value;
         this.closePositionPicker();
      },
      // 初始化扫码监听
      initScanListener() {
         // 在uni-app中,可以通过ref获取DOM节点,但要注意平台差异
         // 这里我们通过一个安全的方法来聚焦输入框
         this.$nextTick(() => {
            const input = this.$refs.scanInput;
            if (input && typeof input.focus === 'function') {
               input.focus();
            }
         })
         });
      },
      
      // 设置扫码监听
      setupScanListener() {
         // 移除之前的监听
         this.removeScanListener();
      // 聚焦扫码输入框
      focusScanInput() {
         // 使用uni-app的安全方法
         this.$nextTick(() => {
            const input = this.$refs.scanInput;
            if (input) {
               // 在uni-app中,input可能是一个组件实例
               // 我们需要获取其DOM节点或使用平台特定的方法
               // 这里我们尝试调用focus,如果不可用则跳过
               try {
                  if (typeof input.focus === 'function') {
                     input.focus();
                  } else if (input.$el && typeof input.$el.focus === 'function') {
                     // 如果是Vue组件实例
                     input.$el.focus();
                  }
               } catch (error) {
                  console.log('聚焦失败,但不影响功能:', error);
               }
            }
         });
      },
      // 处理扫码输入
      handleMaterialScan(e) {
         const value = e.detail ? e.detail.value : e.target.value;
         
         // 监听键盘输入事件
         document.addEventListener('keydown', this.handleKeyDown);
      },
      // 移除监听
      removeScanListener() {
         document.removeEventListener('keydown', this.handleKeyDown);
         // 防抖处理
         if (this.scanTimeout) {
            clearTimeout(this.scanTimeout);
         }
         this.scanTimeout = setTimeout(() => {
            if (value && value.trim()) {
               this.parseBarcodeContent(value.trim());
               this.showScanModal = true;
               this.scanBuffer = ''; // 清空缓冲区
               // 每次打开弹窗时重置位置选择为默认值
               this.positionIndex = 0;
               this.tempMaterial.position = '1';
            }
         }, 300);
      },
      
      // 键盘事件处理
      handleKeyDown(e) {
         // 排除功能键(F1-F12等)
         if (e.key.length > 1 && !['Enter', 'Tab'].includes(e.key)) {
      // 解析条码内容
      parseBarcodeContent(content) {
          // const lines = content.split(/\\n/).filter(line => line.trim() !== '');
         const lines = content.split(' ').filter(line => line.trim().length > 0);
          // 清空临时数据
          this.tempMaterial = {
              model: '',
              batch: '',
              matnr: '',
              rollExtent: '',
              weight: '',
              roughWeight: '',
              joint: 0,
            position: '1' // 重置位置为默认值
          };
          // 使用正则表达式提取键值对[6,7](@ref)
          const extractKeyValue = (line) => {
              const match = line.match(/([^:]+):(.+)/);
              if (match && match.length === 3) {
                  return {
                      key: match[1].trim(),
                      value: match[2].trim()
                  };
              }
              return null;
          };
          // 提取数字(包括小数)[6,7](@ref)
          const extractNumber = (str) => {
              const match = str.match(/[\d.]+/);
              return match ? match[0] : '';
          };
          // 遍历每一行,解析键值对
          lines.forEach(line => {
              const keyValue = extractKeyValue(line);
              if (keyValue) {
                  const { key, value } = keyValue;
                  switch (key) {
                      case '卷号':
                          this.tempMaterial.model = value;
                          break;
                      case '箱号':
                          this.tempMaterial.batch = value;
                          break;
                      case '规格':
                          this.tempMaterial.matnr = value;
                          break;
                      case '长度':
                          this.tempMaterial.rollExtent = extractNumber(value);
                          break;
                      case '净重':
                          this.tempMaterial.weight = extractNumber(value);
                          break;
                      case '毛重':
                          this.tempMaterial.roughWeight = extractNumber(value);
                          break;
                      case '接头':
                          this.tempMaterial.joint = extractNumber(value) || 0;
                          break;
                      default:
                          // 可以处理未知键或忽略
                          break;
                  }
              }
          });
      },
      // 确认添加物料
      confirmAddMaterial() {
         this.closePositionPicker();
         if (!this.tempMaterial.model) {
            uni.showToast({
               title: '卷号不能为空',
               icon: 'none'
            });
            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);
            }
         }
         // 检查是否已存在相同物料
         const exists = this.matList.some(item =>
            item.model === this.tempMaterial.model &&
            item.batch === this.tempMaterial.batch
         );
         if (exists) {
            uni.showToast({
               title: '该物料已存在',
               icon: 'none'
            });
            this.closeModal();
            return;
         };
         // 添加到物料列表
         const newMaterial = {
            id: this.materialId++,
            ...this.tempMaterial,
            position: (this.matList.length + 1).toString(),
            cutting: 2,
            qualified: 0
         };
         this.matList.push(newMaterial);
         uni.showToast({
            title: '物料添加成功',
            icon: 'success'
         });
         this.closeModal();
      },
      
      // 处理扫码结果
      processScanCode() {
         if (!this.scanBuffer) return;
      // 取消添加
      cancelAddMaterial() {
         this.closeModal();
      },
      // 关闭弹窗
      closeModal() {
         this.showPositionPicker = false;
         this.showScanModal = false;
         this.tempMaterial = {
            model: '',
            batch: '',
            matnr: '',
            rollExtent: '',
            weight: '',
            roughWeight: '',
            joint: 0,
            position: '1' // 重置位置
         };
         
         const scanResult = this.scanBuffer.trim();
         this.scanBuffer = '';
         // 重置位置选择
         this.positionIndex = 0;
         
         // 判断扫码长度决定填充位置
         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;
            this.focusScanInput();
         }, 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);
         }
      // 删除物料
      removeMaterial(index) {
         uni.showModal({
            title: '确认删除',
            content: '确定要删除这个物料吗?',
            success: (res) => {
               if (res.confirm) {
                  this.matList.splice(index, 1);
                  uni.showToast({
                     title: '删除成功',
                     icon: 'success'
                  });
               }
            }
         });
      },
      
      // 智能填充物料字段
      smartFillMaterialFields(code) {
         // 根据业务逻辑判断,这里假设长条码可能是卷号或箱号
         // 如果卷号为空,优先填充卷号
         if (!this.matList.model) {
            this.matList.model = code;
         }
         // 如果卷号已有值但箱号为空,填充箱号
         else if (!this.matList.batch) {
            this.matList.batch = code;
         }
         // 如果两者都有值,提示用户
         else {
      // 检查登录状态
      checkLoginStatus() {
         this.token = uni.getStorageSync('token');
         this.baseUrl = uni.getStorageSync('baseUrl');
         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();
               
               uni.showToast({
                  title: '物料信息扫码成功',
                  icon: 'success',
                  duration: 2000
               });
               if (res.data && res.data.code === 200 && 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() !== '');
                  if (this.mode.length === 0) {
                     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'
               });
            }
@@ -406,134 +637,221 @@
      
      modePickerChange(e) {
         this.index_mode = e.detail.value;
         this.boxType = this.mode[this.index_mode]; // 同步到boxType
         this.boxType = this.mode[this.index_mode];
      },
      
      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 = '';
         // 重置物料信息
         this.matList = {
            matnr: '',
            maktx: '',
            batch: '',
            model: '',
            position: '1',
            weight: '',
            roughWeight: '',
            anfme: 1.0,
            rollExtent: '',
            joint: 0,
            cutting: 2,
            qualified: 0,
            modelFront: '',
            batchFront: ''
         };
      },
      // 提交数据
      submit() {
         // 校验必填项
         if (!this.checkLoginStatus()) return;
         // 验证必填项
         if (!this.barcode) {
            uni.showToast({title: '请扫描托盘编码', icon: 'none'});
            return;
         }
         this.boxType = 999;
         if (!this.boxType) {
            uni.showToast({title: '请选择箱型', icon: 'none'});
            return;
         }
         
         if (!this.matList.model || !this.matList.batch) {
            uni.showToast({title: '请填写卷号和箱号', icon: 'none'});
         if (this.matList.length === 0) {
            uni.showToast({title: '请至少添加一个物料', icon: 'none'});
            return;
         }
         
         // 数据类型转换
         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
         };
         // 验证每个物料的必填字段
         for (let i = 0; i < this.matList.length; i++) {
            const mat = this.matList[i];
            if (!mat.model) {
               uni.showToast({title: `物料${i+1}卷号不能为空`, icon: 'none'});
               return;
            }
            if (!mat.batch) {
               uni.showToast({title: `物料${i+1}箱号不能为空`, icon: 'none'});
               return;
            }
         }
         
         // 组装提交数据
         this.submitData = {
         // 准备提交数据
         const submitData = {
            barcode: this.barcode,
            palletizingNo: this.palletizingNo,
            boxType: this.boxType,
            matList: [matItem]
            matList: this.matList.map(mat => ({
               ...mat,
               weight: mat.weight ? Number(mat.weight) : null,
               roughWeight: mat.roughWeight ? Number(mat.roughWeight) : null,
               rollExtent: mat.rollExtent ? Number(mat.rollExtent) : null,
               joint: mat.joint ? Number(mat.joint) : 0,
               position: mat.position // 包含位置信息
            }))
         };
         
         console.log('提交数据:', this.submitData);
         console.log('提交数据:', submitData);
         
         // 提交请求
         // 发送请求
         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();
               
               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.msg || '提交失败',
                     icon: 'none'
                  });
               }
            },
            fail: (err) => {
               console.error('请求失败详情:', err);
               uni.showToast({title: '网络请求失败', icon: 'none'});
               uni.hideLoading();
               console.error('提交失败:', err);
               uni.showToast({
                  title: '网络错误',
                  icon: 'none'
               });
            }
         });
      },
      // 重置表单
      resetForm() {
         this.barcode = '';
         this.boxType = '';
         this.index_mode = 0;
         this.matList = [];
         this.materialId = 1;
      }
   }
}
</script>
<style scoped>
/* 全局样式 */
/* 物料位置选择器样式 */
.full-width {
   grid-column: 1 / -1; /* 占满整行 */
   margin-top: 8rpx;
   position: relative;
   z-index: 100; /* 确保在弹窗内部层级正常 */
}
.position-selector {
   width: 200rpx;
   display: flex;
   justify-content: flex-end;
}
.position-picker {
   width: 200rpx;
}
.position-display {
   display: flex;
   align-items: center;
   justify-content: space-between;
   padding: 8rpx 16rpx;
   border: 1rpx solid #e5e7eb;
   border-radius: 6rpx;
   background-color: #ffffff;
   color: #333;
   font-size: 28rpx;
   min-height: 40rpx;
   width: 100%;
   box-sizing: border-box;
}
.placeholder {
   color: #999;
}
.position-arrow {
   font-size: 20rpx;
   color: #666;
   margin-left: 8rpx;
}
/* 全局样式 - 保持原有风格 */
.sockpage {
   background-color: #f5f7fa;
   padding: 24rpx;
   padding: 20rpx;
   min-height: 100vh;
}
/* 表单列容器 */
.column {
   display: flex;
   flex-direction: column;
   gap: 24rpx;
   gap: 20rpx;
}
/* 表单卡片 */
.form-card {
   background: #ffffff;
   border-radius: 12rpx;
   box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
   padding: 24rpx;
   transition: all 0.2s ease;
   border-radius: 8rpx;
   padding: 20rpx;
   box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.1);
}
/* 物料信息卡片特殊样式 */
.form-row {
   display: flex;
   align-items: center;
   gap: 20rpx;
}
.label {
   font-size: 28rpx;
   color: #333;
   font-weight: 500;
   min-width: 140rpx;
}
.required {
   color: #e64340;
   margin-right: 4rpx;
}
.picker-wrap {
   flex: 1;
}
.input-box {
   border: 1rpx solid #e5e7eb;
   border-radius: 6rpx;
   padding: 16rpx 20rpx;
   background: #fff;
}
.input-placeholder {
   color: #999;
   font-size: 28rpx;
}
.input-value {
   color: #333;
   font-size: 28rpx;
}
/* 物料信息卡片 */
.material-card {
   padding: 0;
}
@@ -541,168 +859,386 @@
.card-title {
   font-size: 30rpx;
   font-weight: 500;
   color: #1f2937;
   padding: 24rpx 24rpx 16rpx;
   color: #333;
   padding: 20rpx 20rpx 16rpx;
   border-bottom: 1rpx solid #f0f0f0;
}
/* 表单行 */
.form-row {
/* 物料列表 */
.material-list {
   padding: 0 20rpx;
}
.material-item {
   margin-bottom: 20rpx;
}
.material-header {
   display: flex;
   flex-direction: row;
   align-items: center;
   gap: 16rpx;
}
/* 标签样式 */
.label {
   font-size: 28rpx;
   color: #374151;
   font-weight: 500;
   min-width: 140rpx;
}
/* 必填项标红 */
.required {
   color: #ef4444;
   margin-right: 4rpx;
}
/* 选择器容器 */
.picker-wrap {
   flex: 1;
   display: flex;
   align-items: center;
   justify-content: space-between;
   align-items: center;
   margin-bottom: 16rpx;
   padding-bottom: 12rpx;
   border-bottom: 1rpx solid #f0f0f0;
}
/* 选择器hover态 */
.picker-hover {
   background-color: #f9fafb;
   border-radius: 8rpx;
}
/* 输入框容器 */
.input-box {
   flex: 1;
.material-title {
   font-size: 28rpx;
   padding: 16rpx 12rpx;
   border: 1rpx solid #e5e7eb;
   border-radius: 8rpx;
   transition: border-color 0.2s ease;
   font-weight: 500;
   color: #333;
}
.input-box:focus-within {
   border-color: #f97316;
.material-actions {
   display: flex;
   gap: 20rpx;
}
/* 输入值样式 */
.input-value {
   color: #1f2937;
}
/* 扫码图标 */
.scan-icon {
.action-btn {
   font-size: 24rpx;
   margin-left: 8rpx;
   color: #6b7280;
   color: #e64340;
   padding: 6rpx 12rpx;
   border-radius: 4rpx;
   background: #fff0f0;
}
/* 物料信息网格布局 */
/* 物料网格 */
.material-grid {
   display: grid;
   grid-template-columns: repeat(2, 1fr);
   gap: 20rpx;
   padding: 24rpx;
   gap: 16rpx;
}
/* 物料信息项 */
.material-item {
   display: flex;
   flex-direction: column;
   gap: 8rpx;
}
/* 物料标签 */
.material-label {
   font-size: 26rpx;
   color: #4b5563;
   color: #666;
}
/* 物料输入框 */
.material-input {
   font-size: 28rpx;
   color: #1f2937;
   padding: 16rpx 12rpx;
   color: #333;
   padding: 12rpx;
   border: 1rpx solid #e5e7eb;
   border-radius: 8rpx;
   border-radius: 6rpx;
   background-color: #ffffff;
   transition: border-color 0.2s ease;
}
.material-input:focus {
   border-color: #f97316;
   outline: none;
/* 空状态 */
.empty-state {
   text-align: center;
   padding: 40rpx 20rpx;
}
/* 按钮组 */
.btn-group {
   display: flex;
   gap: 20rpx;
   justify-content: center;
   margin-top: 16rpx;
   padding: 8rpx 0;
.empty-icon {
   font-size: 60rpx;
   display: block;
   margin-bottom: 20rpx;
   opacity: 0.5;
}
/* 操作按钮通用样式 */
.operate-btn {
   flex: 1;
   max-width: 200rpx;
   height: 80rpx;
   line-height: 80rpx;
.empty-text {
   display: block;
   font-size: 28rpx;
   font-weight: 500;
   border-radius: 12rpx;
   border: none;
   transition: all 0.2s ease;
   display: flex;
   align-items: center;
   justify-content: center;
   color: #999;
   margin-bottom: 10rpx;
}
.empty-tip {
   display: block;
   font-size: 24rpx;
   color: #ccc;
}
/* 扫码按钮 */
.scan-btn {
   background-color: #3b82f6;
   color: #ffffff;
   display: flex;
   align-items: center;
   justify-content: center;
   gap: 10rpx;
   padding: 20rpx;
   margin: 20rpx 20rpx 20rpx;
   background: #f0f7ff;
   border: 1rpx dashed #1989fa;
   border-radius: 8rpx;
   color: #1989fa;
   font-size: 28rpx;
}
.scan-btn:active {
   background-color: #2563eb;
   transform: scale(0.98);
.scan-icon {
   font-size: 36rpx;
   font-weight: bold;
}
/* 提交按钮 */
.submit-btn {
   background-color: #f97316;
   color: #ffffff;
/* 按钮组 */
.btn-group {
   margin-top: 20rpx;
   padding: 8rpx 0;
   text-align: center;
}
.submit-btn:active {
   background-color: #ea580c;
   transform: scale(0.98);
.operate-btn {
   width: 200rpx;
   height: 80rpx;
   line-height: 80rpx;
   font-size: 30rpx;
   font-weight: 500;
   border-radius: 8rpx;
   border: none;
   background: #333;
   color: #fff;
}
/* 适配小屏幕 */
@media (max-width: 375px) {
/* 弹窗样式 */
.modal-mask {
   position: fixed;
   top: 0;
   left: 0;
   right: 0;
   bottom: 0;
   background: rgba(0, 0, 0, 0.5);
   display: flex;
   align-items: center;
   justify-content: center;
   z-index: 1000;
}
.modal-content {
   background: #fff;
   border-radius: 12rpx;
   width: 600rpx;
   max-width: 90%;
   overflow: hidden;
}
.modal-header {
   padding: 30rpx 30rpx 20rpx;
   border-bottom: 1rpx solid #f0f0f0;
}
.modal-title {
   font-size: 32rpx;
   font-weight: 500;
   color: #333;
}
.modal-body {
   padding: 30rpx;
}
.scan-preview {
   margin-bottom: 20rpx;
   padding-bottom: 20rpx;
   border-bottom: 1rpx solid #f0f0f0;
}
.scan-label {
   font-size: 28rpx;
   color: #666;
   display: block;
   margin-bottom: 10rpx;
}
.scan-code {
   font-size: 32rpx;
   color: #333;
   font-weight: bold;
   word-break: break-all;
}
.preview-grid {
   display: grid;
   grid-template-columns: repeat(2, 1fr);
   gap: 15rpx;
}
.preview-item {
   display: flex;
   justify-content: space-between;
   align-items: center;
}
.preview-label {
   font-size: 28rpx;
   color: #666;
}
.preview-value {
   font-size: 28rpx;
   color: #333;
   font-weight: 500;
}
.modal-footer {
   display: flex;
   border-top: 1rpx solid #f0f0f0;
}
.modal-btn {
   flex: 1;
   height: 90rpx;
   line-height: 90rpx;
   font-size: 30rpx;
   border: none;
   background: transparent;
   border-radius: 0;
}
.modal-btn.cancel {
   color: #999;
   border-right: 1rpx solid #f0f0f0;
}
.modal-btn.confirm {
   color: #1989fa;
   font-weight: 500;
}
/* 自定义选择器样式 */
.custom-picker-overlay {
   position: fixed;
   top: 0;
   left: 0;
   right: 0;
   bottom: 0;
   background-color: rgba(0, 0, 0, 0.5);
   display: flex;
   align-items: center;
   justify-content: center;
   z-index: 9999; /* 确保在最上层 */
}
.custom-picker-content {
   background-color: #ffffff;
   border-radius: 12rpx;
   width: 600rpx;
   max-width: 80%;
   max-height: 70vh;
   overflow: hidden;
   box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.15);
   animation: fadeIn 0.3s ease;
}
@keyframes fadeIn {
   from {
      opacity: 0;
      transform: translateY(20rpx);
   }
   to {
      opacity: 1;
      transform: translateY(0);
   }
}
.custom-picker-header {
   display: flex;
   justify-content: space-between;
   align-items: center;
   padding: 30rpx;
   border-bottom: 1rpx solid #f0f0f0;
}
.custom-picker-title {
   font-size: 32rpx;
   font-weight: 500;
   color: #333;
}
.custom-picker-close {
   font-size: 40rpx;
   color: #999;
   line-height: 1;
   padding: 0 10rpx;
   cursor: pointer;
}
.custom-picker-list {
   max-height: 50vh;
   overflow-y: auto;
   padding: 0;
}
.custom-picker-item {
   display: flex;
   justify-content: space-between;
   align-items: center;
   padding: 25rpx 30rpx;
   border-bottom: 1rpx solid #f5f5f5;
   cursor: pointer;
   transition: background-color 0.2s;
}
.custom-picker-item:last-child {
   border-bottom: none;
}
.custom-picker-item:hover {
   background-color: #f8f9fa;
}
.custom-picker-item.selected {
   background-color: #f0f7ff;
   color: #1989fa;
}
.custom-picker-text {
   font-size: 30rpx;
}
.custom-picker-check {
   font-size: 28rpx;
   color: #1989fa;
   font-weight: bold;
}
/* 弹窗样式调整,确保层级关系 */
.modal-mask {
   position: fixed;
   top: 0;
   left: 0;
   right: 0;
   bottom: 0;
   background: rgba(0, 0, 0, 0.5);
   display: flex;
   align-items: center;
   justify-content: center;
   z-index: 1000; /* 弹窗基础层级 */
}
.modal-content {
   position: relative; /* 确保子元素相对定位 */
   z-index: 1001; /* 比蒙版高一级 */
   background: #fff;
   border-radius: 12rpx;
   width: 600rpx;
   max-width: 90%;
   overflow: visible; /* 改为 visible 允许选择器弹出 */
}
/* 响应式适配 */
@media (max-width: 750rpx) {
   .material-grid {
      grid-template-columns: 1fr;
   }
   
   .operate-btn {
      max-width: 160rpx;
      height: 72rpx;
      line-height: 72rpx;
      font-size: 26rpx;
   .preview-grid {
      grid-template-columns: 1fr;
   }
   .modal-content {
      width: 90%;
   }
   .custom-picker-content {
      width: 90%;
      max-width: 90%;
   }
   .custom-picker-item {
      padding: 20rpx 25rpx;
   }
}
</style>