feat: 新增托盘库存网格(任务6)

This commit is contained in:
zhouz
2026-08-08 15:22:42 +08:00
parent ec1aca6a35
commit 0a2767e228

View File

@@ -0,0 +1,168 @@
<script lang="ts" setup>
import type { LmsStockingIvtApi } from '#/api/lms/stockingivt';
import { computed, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { Spin, Tooltip } from 'antdv-next';
import { getPaperVehicleInventory } from '#/api/lms/stockingivt';
interface PalletCell {
colNum: number;
inventory?: LmsStockingIvtApi.PaperVehicleInventory;
rowNum: number;
}
const vehicleCode = ref('');
const inventoryList = ref<LmsStockingIvtApi.PaperVehicleInventory[]>([]);
const loading = ref(false);
const modalTitle = computed(() => `托盘 ${vehicleCode.value} 的库存`);
const occupiedCount = computed(() => inventoryList.value.length);
const cells = computed<PalletCell[]>(() => {
const inventoryMap = new Map(
inventoryList.value.map((item) => [`${item.rowNum}:${item.colNum}`, item]),
);
return Array.from({ length: 25 }, (_, index) => {
const rowNum = Math.floor(index / 5) + 1;
const colNum = (index % 5) + 1;
return {
colNum,
inventory: inventoryMap.get(`${rowNum}:${colNum}`),
rowNum,
};
});
});
const [Modal, modalApi] = useVbenModal({
footer: false,
async onOpenChange(isOpen) {
if (!isOpen) {
vehicleCode.value = '';
inventoryList.value = [];
return;
}
const data = modalApi.getData<{ vehicleCode: string }>();
if (!data?.vehicleCode) return;
vehicleCode.value = data.vehicleCode;
loading.value = true;
try {
inventoryList.value = await getPaperVehicleInventory(data.vehicleCode);
} finally {
loading.value = false;
}
},
});
</script>
<template>
<Modal class="w-[900px] max-w-[96vw]" :title="modalTitle">
<Spin :spinning="loading">
<div class="px-3 pb-4">
<div class="mb-4 flex justify-end gap-6 text-sm">
<span>已占用 {{ occupiedCount }} / 25</span>
<span>空位 {{ 25 - occupiedCount }}</span>
</div>
<div class="pallet-grid">
<div></div>
<div v-for="col in 5" :key="`header-${col}`" class="axis-label">
{{ col }}
</div>
<template v-for="row in 5" :key="`row-${row}`">
<div class="axis-label"> {{ row }}</div>
<Tooltip
v-for="cell in cells.slice((row - 1) * 5, row * 5)"
:key="`${cell.rowNum}-${cell.colNum}`"
>
<template #title>
<div>位置 {{ cell.rowNum }} {{ cell.colNum }} </div>
<template v-if="cell.inventory">
<div>物料编码{{ cell.inventory.materialCode }}</div>
<div>物料名称{{ cell.inventory.materialName }}</div>
<div>数量{{ cell.inventory.qty }}</div>
</template>
<div v-else>空位</div>
</template>
<div :class="['pallet-cell', { occupied: cell.inventory }]">
<template v-if="cell.inventory">
<strong>{{ cell.inventory.materialCode }}</strong>
<span>{{ cell.inventory.materialName }}</span>
</template>
<span v-else></span>
</div>
</Tooltip>
</template>
</div>
<div class="mt-4 flex gap-6 text-sm text-gray-500">
<span class="legend"><i class="legend-block occupied"></i>有物料1 </span>
<span class="legend"><i class="legend-block"></i>空位</span>
</div>
</div>
</Spin>
</Modal>
</template>
<style scoped>
.pallet-grid {
display: grid;
grid-template-columns: 48px repeat(5, minmax(100px, 1fr));
gap: 8px;
align-items: stretch;
}
.axis-label {
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
}
.pallet-cell {
display: flex;
min-height: 72px;
flex-direction: column;
align-items: center;
justify-content: center;
overflow: hidden;
padding: 8px;
color: #999;
text-align: center;
border: 1px dashed #d9d9d9;
border-radius: 6px;
background: #fafafa;
}
.pallet-cell.occupied {
color: #1f1f1f;
border: 1px solid #91caff;
background: #e6f4ff;
}
.pallet-cell span {
max-width: 100%;
overflow: hidden;
font-size: 12px;
text-overflow: ellipsis;
white-space: nowrap;
}
.legend {
display: inline-flex;
align-items: center;
}
.legend-block {
width: 13px;
height: 13px;
margin-right: 6px;
border: 1px dashed #d9d9d9;
background: #fafafa;
}
.legend-block.occupied {
border: 1px solid #91caff;
background: #e6f4ff;
}
</style>