yangg 3 hete
szülő
commit
9bc3578af2

+ 359 - 14
src/views/system/Inventorycount/InventoryDialog/index.vue

@@ -2,9 +2,25 @@
     <el-dialog v-model="visible" title="开始盘点" width="800px" @open="onOpen" @opened="onOpened" @close="onClose">
       <div>
         <div class="form-row">
-          <el-input ref="deviceCodeInputRef" v-model="device_code" placeholder="设备编号" @keyup.enter="handleAdd" :disabled="loading" />
-          <el-input v-model="check_no" placeholder="盘点编号" @keyup.enter="handleAdd" :disabled="true" />
-          <el-button type="primary" @click="handleAdd" :loading="loading">添加</el-button>
+          <el-input ref="deviceCodeInputRef" v-model="device_code" placeholder="设备编号(支持扫码枪/RFID批量录入,回车添加)" @keyup.enter="handleAddToBatch" :disabled="loading" />
+          <el-input v-model="check_no" placeholder="盘点编号" @keyup.enter="handleAddToBatch" :disabled="true" />
+          <el-button type="success" @click="handleAddToBatch" :disabled="loading">添加到待提交列表</el-button>
+          <el-button type="primary" @click="handleBatchSubmit" :loading="loading" :disabled="pendingDeviceCodes.length === 0">批量提交({{ pendingDeviceCodes.length }})</el-button>
+        </div>
+
+        <!-- 待提交设备编号列表 -->
+        <div v-if="pendingDeviceCodes.length > 0" class="pending-list" style="margin-top: 15px; margin-bottom: 15px;">
+          <div class="pending-title">待提交设备编号 ({{ pendingDeviceCodes.length }}个):</div>
+          <el-tag 
+            v-for="(code, index) in pendingDeviceCodes" 
+            :key="index" 
+            closable 
+            @close="removePendingCode(index)"
+            style="margin-right: 8px; margin-bottom: 8px;"
+          >
+            {{ code }}
+          </el-tag>
+          <el-button type="text" size="small" @click="clearPendingCodes" style="margin-left: 10px;">清空</el-button>
         </div>
     
         <el-table :data="deviceList"  v-loading="loading" style="margin-top: 20px">
@@ -14,6 +30,7 @@
           <el-table-column prop="device_name" label="设备名称" />
           <el-table-column prop="device_category" label="设备分类" />
           <el-table-column prop="check_no" label="盘点编号" />
+          <el-table-column prop="scanned_quantity" label="盘点数量" />
           <el-table-column prop="status_display" label="状态" />
           <!-- <el-table-column prop="productName" label="商品名称" /> -->
         </el-table>
@@ -40,6 +57,8 @@
   const currentRecordId = ref<number | null>(null);
   const loading = ref(false);
   const deviceCodeInputRef = ref<any>(null);
+  // 待提交的设备编号数组
+  const pendingDeviceCodes = ref<string[]>([]);
 //   const btflag=ref(true);
   
   const setCheckNo = (val: string) => {
@@ -92,6 +111,7 @@
     // check_no.value = '';
     deviceList.value = [];
     currentRecordId.value = null;
+    pendingDeviceCodes.value = [];
   };
 
   // 对话框完全打开后聚焦输入框
@@ -105,8 +125,315 @@
         return list.find(item => item.check_no === checkNo);
     };
 
+  /**
+   * 添加或更新设备记录,如果存在相同设备编号则替换为最新数据
+   * @param newItems 新添加的设备记录数组
+   */
+  const addOrUpdateDeviceList = (newItems: any[]) => {
+    newItems.forEach((newItem) => {
+      // 查找是否已存在相同的设备编号
+      const existingIndex = deviceList.value.findIndex(
+        (item) => item.device_code === newItem.device_code && item.check_no === newItem.check_no
+      );
+
+      if (existingIndex !== -1) {
+        // 如果已存在,替换为最新数据(保留最新的那条)
+        deviceList.value[existingIndex] = {
+          ...newItem,
+          scanned_quantity: newItem.scanned_quantity || 1,
+        };
+      } else {
+        // 如果不存在,添加新记录
+        deviceList.value.push({
+          ...newItem,
+          scanned_quantity: newItem.scanned_quantity || 1,
+        });
+      }
+    });
+  };
+
+  /**
+   * 分割单个字符串中的RFID粘连标签(不包含空格)
+   * @param input 输入的设备编号字符串(已去除空格)
+   * @returns 分割后的设备编号数组
+   */
+  const splitRFIDStickyCodes = (input: string): string[] => {
+    if (!input) return [];
+
+    // 方法1: 使用正则表达式匹配RFID标签(E280开头,24字符)
+    // 匹配以E280开头的24字符标签(E280 + 20个十六进制字符)
+    const rfidPattern = /E280[0-9A-F]{20}/gi;
+    const matches = input.match(rfidPattern);
+    
+    // 如果匹配到多个RFID标签,说明是粘连的,返回分割后的数组
+    if (matches && matches.length > 1) {
+      return matches;
+    }
+
+    // 方法2: 如果长度是24的倍数且大于24,按固定长度分割
+    // 这种情况适用于粘连的RFID标签(无论是否以E280开头)
+    if (input.length > 24 && input.length % 24 === 0) {
+      const codes: string[] = [];
+      for (let i = 0; i < input.length; i += 24) {
+        const code = input.substring(i, i + 24);
+        if (code.trim()) {
+          codes.push(code);
+        }
+      }
+      // 如果分割出多个,返回分割结果
+      if (codes.length > 1) {
+        return codes;
+      }
+    }
+
+    // 如果都不匹配,返回原始输入作为单个设备编号
+    return [input];
+  };
+
+  /**
+   * 分割RFID粘连的设备编号
+   * 支持空格分隔和粘连两种情况
+   * RFID标签通常以特定前缀开头(如E280),且有固定长度(24字符)
+   * @param input 输入的设备编号字符串
+   * @returns 分割后的设备编号数组
+   */
+  const splitRFIDCodes = (input: string): string[] => {
+    const trimmed = input.trim();
+    if (!trimmed) return [];
+
+    // 方法0: 优先处理空格分隔的情况
+    // 如果输入中包含空格,先按空格分割,然后对每个部分处理粘连
+    if (trimmed.includes(' ')) {
+      const parts = trimmed.split(/\s+/).filter(part => part.trim()); // 按空格分割,过滤空字符串
+      const result: string[] = [];
+      
+      // 对每个部分处理可能的粘连情况
+      for (const part of parts) {
+        const splitCodes = splitRFIDStickyCodes(part.trim());
+        result.push(...splitCodes);
+      }
+      
+      // 如果分割出多个,返回结果
+      if (result.length > 1) {
+        return result;
+      }
+      // 如果只有一个结果,继续后续处理
+      return result.length > 0 ? result : [trimmed];
+    }
+
+    // 如果没有空格,直接处理粘连情况
+    return splitRFIDStickyCodes(trimmed);
+  };
+
+  // 添加到待提交列表(不立即提交)
+  const handleAddToBatch = () => {
+    if (!device_code.value || !check_no.value) {
+      ElMessage.warning('请输入设备编号');
+      return;
+    }
+
+    const input = device_code.value.trim();
+    if (!input) {
+      ElMessage.warning('设备编号不能为空');
+      return;
+    }
+
+    // 自动分割RFID粘连的设备编号
+    const codes = splitRFIDCodes(input);
+    
+    if (codes.length === 0) {
+      ElMessage.warning('未识别到有效的设备编号');
+      return;
+    }
+
+    // 批量添加到待提交列表(允许重复)
+    pendingDeviceCodes.value.push(...codes);
+    
+    // 显示提示信息
+    if (codes.length > 1) {
+      ElMessage.success(`已自动分割并添加 ${codes.length} 个设备编号`);
+    }
+    
+    device_code.value = '';
+    
+    // 自动聚焦输入框,方便继续扫码
+    focusDeviceCodeInput();
+  };
+
+  // 移除待提交的设备编号
+  const removePendingCode = (index: number) => {
+    pendingDeviceCodes.value.splice(index, 1);
+  };
+
+  // 清空待提交列表
+  const clearPendingCodes = () => {
+    pendingDeviceCodes.value = [];
+  };
+
+  // 批量提交设备编号
+  const handleBatchSubmit = async () => {
+    if (pendingDeviceCodes.value.length === 0 || !check_no.value) {
+      ElMessage.warning('没有待提交的设备编号');
+      return;
+    }
+
+    loading.value = true;
+    const successList: any[] = [];
+    const failList: string[] = [];
+
+    try {
+      // 优先使用批量提交接口
+      const batchPayload = {
+        check_no: check_no.value,
+        device_codes: [...pendingDeviceCodes.value], // 复制数组,避免修改原数组
+      };
+
+      try {
+        const batchRes = await api.ScanDeviceBatch(batchPayload);
         
+        if (batchRes?.code === 2000) {
+          // 批量提交成功,处理返回数据
+          const reslist = await api.GetPermission();
+          if (reslist?.code === 2000) {
+            const record = findCheckRecord(check_no.value, reslist.data);
+            currentRecordId.value = record?.id ?? null;
+
+            // 处理批量返回的数据
+            // 如果后端返回了详细的设备信息列表,使用返回的数据
+            if (batchRes.data && Array.isArray(batchRes.data)) {
+              // 后端返回了设备详情数组
+              batchRes.data.forEach((item: any) => {
+                successList.push({
+                  device_code: item.device_code || item.deviceCode,
+                  check_no: check_no.value,
+                  productName: item.productName || '未知商品',
+                  id: record?.id || 0,
+                  status_display: record?.status_display || '',
+                  device_name: item.device_name || item.deviceName || '未知设备',
+                  device_category: item.device_category || item.deviceCategory || '未知分类',
+                  scanned_quantity: item.scanned_quantity || 0,
+                });
+              });
+            } else if (batchRes.data?.success_codes && Array.isArray(batchRes.data.success_codes)) {
+              // 后端只返回了成功的设备编号列表
+              batchRes.data.success_codes.forEach((code: string) => {
+                successList.push({
+                  device_code: code,
+                  check_no: check_no.value,
+                  productName: '未知商品',
+                  id: record?.id || 0,
+                  status_display: record?.status_display || '',
+                  device_name: '未知设备',
+                  device_category: '未知分类',
+                  scanned_quantity: 1,
+                });
+              });
+              
+              // 处理失败的设备编号
+              if (batchRes.data?.fail_codes && Array.isArray(batchRes.data.fail_codes)) {
+                failList.push(...batchRes.data.fail_codes);
+              }
+            } else {
+              // 如果后端没有返回详细数据,假设全部成功
+              pendingDeviceCodes.value.forEach((code: string) => {
+                successList.push({
+                  device_code: code,
+                  check_no: check_no.value,
+                  productName: '未知商品',
+                  id: record?.id || 0,
+                  status_display: record?.status_display || '',
+                  device_name: '未知设备',
+                  device_category: '未知分类',
+                  scanned_quantity: 1,
+                });
+              });
+            }
+
+            // 将成功的数据添加到列表(如果存在相同设备编号,保留最新的)
+            addOrUpdateDeviceList(successList);
+
+            // 显示提交结果并处理待提交列表
+            if (successList.length > 0 && failList.length === 0) {
+              // 全部成功,清空待提交列表
+              pendingDeviceCodes.value = [];
+              ElMessage.success(`提交成功`);
+            } else if (successList.length > 0 && failList.length > 0) {
+              // 部分成功,保留失败的设备编号
+              pendingDeviceCodes.value = failList;
+              ElMessage.warning(`成功提交 ${successList.length} 个,失败 ${failList.length} 个`);
+            } else {
+              // 全部失败,保留失败的设备编号
+              pendingDeviceCodes.value = failList;
+              ElMessage.error(`提交失败,共 ${failList.length} 个设备编号`);
+            }
+            return; // 批量提交成功,直接返回
+          }
+        } else {
+          // 批量提交失败,记录错误但继续尝试逐个提交
+          console.warn('批量提交失败,尝试逐个提交:', batchRes);
+        }
+      } catch (batchError) {
+        // 批量接口不存在或出错,回退到逐个提交
+        console.warn('批量提交接口不可用,使用逐个提交方式:', batchError);
+      }
+
+      // 回退方案:如果批量提交失败或接口不存在,使用逐个提交
+      for (const code of pendingDeviceCodes.value) {
+        try {
+          const payload = {
+            device_code: code,
+            check_no: check_no.value,
+          };
+          
+          const res = await api.ScanDevice(payload);
+          if (res?.code === 2000) {
+            const reslist = await api.GetPermission();
+            if (reslist?.code === 2000) {
+              const record = findCheckRecord(check_no.value, reslist.data);
+              successList.push({
+                ...payload,
+                productName: res.data?.productName || '未知商品',
+                id: record?.id || 0,
+                status_display: record?.status_display || '',
+                device_name: res.data?.device_name || '未知设备',
+                device_category: res.data?.device_category || '未知分类',
+                scanned_quantity: res.data?.scanned_quantity || 1,
+              });
+              currentRecordId.value = record?.id ?? null;
+            }
+          } else {
+            failList.push(code);
+          }
+        } catch (error) {
+          console.error(`提交设备编号 ${code} 失败:`, error);
+          failList.push(code);
+        }
+      }
+
+      // 将成功的数据添加到列表(如果存在相同设备编号,保留最新的)
+      addOrUpdateDeviceList(successList);
 
+      // 显示提交结果并处理待提交列表
+      if (successList.length > 0 && failList.length === 0) {
+        // 全部成功,清空待提交列表
+        pendingDeviceCodes.value = [];
+        ElMessage.success(`成功提交 ${successList.length} 个设备编号`);
+      } else if (successList.length > 0 && failList.length > 0) {
+        // 部分成功,保留失败的设备编号
+        pendingDeviceCodes.value = failList;
+        ElMessage.warning(`成功提交 ${successList.length} 个,失败 ${failList.length} 个`);
+      } else {
+        // 全部失败,保留失败的设备编号
+        pendingDeviceCodes.value = failList;
+        ElMessage.error(`提交失败,共 ${failList.length} 个设备编号`);
+      }
+    } finally {
+      loading.value = false;
+      focusDeviceCodeInput();
+    }
+  };
+
+  // 保留原有的单个添加方法(兼容性)
   const handleAdd = async () => {
     if (!device_code.value || !check_no.value) return;
 
@@ -122,14 +449,16 @@
           const reslist=await api.GetPermission();
           if (reslist?.code===2000) {
               const record = findCheckRecord(check_no.value, reslist.data);
-              deviceList.value.push({
+              // 添加或更新设备记录(如果存在相同设备编号,保留最新的)
+              addOrUpdateDeviceList([{
                   ...payload,
                   productName: res.data?.productName || '未知商品',
                   id: record?.id||0,
                   status_display:record?.status_display||'',
                   device_name: res.data?.device_name || '未知设备',
                   device_category: res.data?.device_category || '未知分类',
-              });
+                  scanned_quantity: res.data?.scanned_quantity || 1,
+              }]);
               device_code.value = '';
               currentRecordId.value = record?.id ?? null;
           }
@@ -197,17 +526,33 @@
   defineExpose({ visible,setCheckNo});
 
   const onClose = () => {
-  check_no.value = '';
-    };
+    check_no.value = '';
+    pendingDeviceCodes.value = [];
+  };
 
 
   </script>
   
-  <style scoped>
-  .form-row {
-    display: flex;
-    gap: 10px;
-    margin-bottom: 10px;
-  }
-  </style>
+<style scoped>
+.form-row {
+  display: flex;
+  gap: 10px;
+  margin-bottom: 10px;
+  flex-wrap: wrap;
+}
+
+.pending-list {
+  padding: 12px;
+  background-color: #f5f7fa;
+  border-radius: 4px;
+  border: 1px solid #e4e7ed;
+}
+
+.pending-title {
+  font-size: 14px;
+  font-weight: 500;
+  color: #606266;
+  margin-bottom: 8px;
+}
+</style>
   

+ 9 - 0
src/views/system/Inventorycount/api.ts

@@ -73,6 +73,15 @@ export function ScanDevice(obj: object) {
 	});
 }
 
+// 批量扫描设备
+export function ScanDeviceBatch(obj: { check_no: string; device_codes: string[] }) {
+	return request({
+		url: '/api/system/device/scan/batch/',
+		method: 'post',
+		data: obj,
+	});
+}
+
 
 export function CompleteScan(obj: object) {
 	return request({