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>
This commit is contained in:
zlwuandCursor committed 2026-07-28 19:15:05 +08:00
1 parent 789c6443f3
commit 9978ae7724
1 file changed
+68 -5
@@ -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;