Просмотр исходного кода

feat(web-admin): add shelf cell checkboxes for batch outbound

Occupied cells show always-on checkboxes; body click still opens detail dialog.

Co-authored-by: Cursor <cursoragent@cursor.com>
zlwu 3 месяцев назад
Родитель
Сommit
9978ae7724
1 измененных файлов с 68 добавлено и 5 удалено
  1. 68 5
      web-admin/src/views/operations/storage-facility/index.vue

+ 68 - 5
web-admin/src/views/operations/storage-facility/index.vue

@@ -168,12 +168,25 @@
                         <span class="add-icon">+</span>
                       </div>
                       <div
-                        v-for="(cell, cellIndex) in row.cells"
+                        v-for="cell in row.cells"
                         :key="cell.id"
                         class="shelf-cell"
-                        :class="{ occupied: cell.slotStatus === '占用' }"
+                        :class="{
+                          occupied: cell.slotStatus === '占用',
+                          'is-outbound-selected': isSlotOutboundSelected(cell.id)
+                        }"
                         @click="handleOpenOccupancyDialog(cell)"
                       >
+                        <button
+                          v-if="isSlotSelectableForOutbound(cell)"
+                          class="cell-check-btn"
+                          type="button"
+                          title="选择出库"
+                          :aria-pressed="isSlotOutboundSelected(cell.id)"
+                          @click="toggleSlotOutboundSelect(cell, $event)"
+                        >
+                          <span class="cell-check-btn__mark">✓</span>
+                        </button>
                         <button
                           class="cell-delete-btn"
                           @click.stop="handleDeleteSlot(cell)"
@@ -1286,13 +1299,10 @@
     return cell?.slotStatus === '占用' && !!(cell?.storedOccupancyId || '').toString().trim()
   }
 
-  // Task 3 shelf-cell checkbox UI will call these helpers
-  // eslint-disable-next-line @typescript-eslint/no-unused-vars
   function isSlotOutboundSelected(slotId: string): boolean {
     return selectedOutboundMap.value.has(slotId)
   }
 
-  // eslint-disable-next-line @typescript-eslint/no-unused-vars
   function toggleSlotOutboundSelect(cell: any, ev?: Event) {
     ev?.stopPropagation?.()
     if (!isSlotSelectableForOutbound(cell)) return
@@ -2660,6 +2670,59 @@
     }
   }
 
+  .cell-check-btn {
+    position: absolute;
+    top: 3px;
+    left: 3px;
+    width: 18px;
+    height: 18px;
+    padding: 0;
+    border: 1.5px solid #cbd5e1;
+    border-radius: 4px;
+    background: #fff;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    cursor: pointer;
+    z-index: 2;
+
+    .cell-check-btn__mark {
+      font-size: 11px;
+      line-height: 1;
+      color: transparent;
+    }
+
+    &:hover {
+      border-color: var(--primary, #10b981);
+    }
+  }
+
+  .shelf-cell.is-outbound-selected {
+    outline: 2px solid var(--primary, #10b981);
+    outline-offset: 1px;
+    background: #ecfdf5;
+    border-color: #a7f3d0;
+
+    &.occupied {
+      background: #ecfdf5;
+      border-color: #a7f3d0;
+
+      .cell-product-name,
+      .cell-product-type {
+        color: var(--primary-dark, #059669);
+      }
+    }
+
+    .cell-check-btn {
+      background: var(--primary, #10b981);
+      border-color: var(--primary, #10b981);
+
+      .cell-check-btn__mark {
+        color: #fff;
+      }
+    }
+  }
+
   .cell-delete-btn {
     position: absolute;
     top: 0;