Files
web/app/components/inventory/InventoryTable.vue
2026-04-14 15:26:00 +07:00

567 lines
13 KiB
Vue

<script setup>
import InventoryRow from '@/components/inventory/InventoryRow.vue';
import SelectedInvItem from '@/components/inventory/SelectedInvItem.vue';
const invItems = [
{
id: 1,
name: 'Dầu gội thảo mộc 500ml',
category: 'Chăm sóc tóc',
sku: 'DG-012',
storage: 'Kho TP.HCM',
storage__position: 'WH-HCM/C-01',
stock: 200,
preorder: 50,
available: 150,
unit_price: '120000.00',
total: '24000000.00',
batch: 'BATCH-2024-012',
expired: '2026-08-20',
status: 'OK',
moveHistory: [
{
id: 1,
type: 1,
type__name: 'Nhập',
date: '2026-04-03',
code: 'PO-009',
to__code: 'WH-HCM/C-01',
delta: 300,
},
{
id: 2,
type: 2,
type__name: 'Xuất',
date: '2026-04-04',
code: 'SO-034',
from__code: 'WH-HCM/C-01',
delta: -100
},
]
},
{
id: 2,
name: 'Gel rửa mặt làm sạch sâu',
category: 'Mỹ phẩm',
sku: 'GRM-006',
storage: 'Kho Hà Nội',
storage__position: 'WH-HN/A-03',
stock: 12,
preorder: 5,
available: 7,
unit_price: '140000.00',
total: '1700000.00',
batch: 'BATCH-2024-006',
expired: '2025-11-20',
status: 'Thấp',
moveHistory: [
{
id: 3,
type: 2,
type__name: 'Xuất',
date: '2026-03-28',
code: 'SO-028',
to__code: 'WH-HN/A-03',
delta: -50,
},
]
},
{
id: 3,
name: 'Kem chống nắng SPF 50+',
category: 'Mỹ phẩm',
sku: 'KCN-001',
storage: 'Kho Hà Nội',
storage__position: 'WH-HN/A-01',
stock: 150,
preorder: 20,
available: 130,
unit_price: '280000.00',
total: '42000000.00',
batch: 'BATCH-2024-001',
expired: '2025-10-15',
status: 'OK',
moveHistory: [
{
id: 4,
type: 1,
type__name: 'Nhập',
date: '2026-04-05',
code: 'PO-001',
from__code: 'WH-HN/A-01',
delta: 200,
},
{
id: 5,
type: 2,
type__name: 'Xuất',
date: '2026-04-06',
code: 'SO-023',
from__code: 'WH-HN/A-01',
delta: -50,
},
]
},
{
id: 4,
name: 'Kem dưỡng ẩm ban đêm',
category: 'Dưỡng da',
sku: 'KD-007',
storage: 'Kho Đà Nẵng',
storage__position: 'WH-DN/A-01',
stock: 8,
preorder: 3,
available: 5,
unit_price: '380000.00',
total: '30040000.00',
batch: 'BATCH-2024-007',
expired: '2025-09-10',
status: 'Thấp',
moveHistory: [
{
id: 6,
type: 2,
type__name: 'Xuất',
date: '2026-03-25',
code: 'SO-029',
from__code: 'WH-DN/A-01',
delta: -40,
},
]
},
{
id: 5,
name: 'Kem mắt chống lão hóa',
category: 'Dưỡng da',
sku: 'KM-011',
storage: 'Kho Hà Nội',
storage__position: 'WH-HN/B-02',
stock: 30,
preorder: 28,
available: 2,
unit_price: '550000.00',
total: '16500000.00',
batch: 'BATCH-2024-011',
expired: '2025-10-05',
status: 'Thấp',
moveHistory: [
{
id: 7,
type: 2,
type__name: 'Xuất',
date: '2026-03-27',
code: 'SO-033',
from__code: 'WH-HN/B-02',
delta: -25,
},
]
},
{
id: 6,
name: 'Mặt nạ collagen 10 miếng',
category: 'Mặt nạ',
sku: 'MN-004',
storage: 'Kho TP.HCM',
storage__position: 'WH-HCM/A-01',
stock: 45,
preorder: 15,
available: 30,
unit_price: '320000.00',
total: '14400000.00',
batch: 'BATCH-2024-004',
expired: '2025-08-15',
status: 'OK',
moveHistory: [
{
id: 8,
type: 1,
type__name: 'Nhập',
date: '2026-03-30',
code: 'PO-004',
to__code: 'WH-HCM/A-01',
delta: 60,
},
{
id: 9,
type: 2,
type__name: 'Xuất',
date: '2026-04-01',
code: 'SO-026',
from__code: 'WH-HCM/A-01',
delta: -15,
},
]
},
{
id: 7,
name: 'Nước tẩy trang 3 trong 1',
category: 'Tẩy trang',
sku: 'NTT-010',
storage: 'Kho Đà Nẵng',
storage__position: 'WH-DN/A-02',
stock: 140,
preorder: 35,
available: 105,
unit_price: '195000.00',
total: '27300000.00',
batch: 'BATCH-2024-010',
expired: '2026-04-30',
status: 'OK',
moveHistory: [
{
id: 10,
type: 1,
type__name: 'Nhập',
date: '2026-03-29',
code: 'PO-008',
to__code: 'WH-DN/A-02',
delta: 200,
},
{
id: 11,
type: 2,
type__name: 'Xuất',
date: '2026-04-01',
code: 'SO-032',
from__code: 'WH-DN/A-02',
delta: -60,
},
]
},
{
id: 8,
name: 'Phấn nền BB cream',
category: 'Trang điểm',
sku: 'PN-009',
storage: 'Kho TP.HCM',
storage__position: 'WH-HCM/B-01',
stock: 65,
preorder: 20,
available: 45,
unit_price: '220000.00',
total: '14300000.00',
batch: 'BATCH-2024-009',
expired: '2025-07-15',
status: 'OK',
moveHistory: [
{
id: 12,
type: 1,
type__name: 'Nhập',
date: '2026-04-01',
code: 'PO-007',
to__code: 'WH-HCM/B-01',
delta: 100,
},
{
id: 13,
type: 2,
type__name: 'Xuất',
date: '2026-04-02',
code: 'SO-031',
from__code: 'WH-HCM/B-01',
delta: -35,
},
]
},
{
id: 9,
name: 'Serum Vitamin C 30ml',
category: 'Dưỡng da',
sku: 'SER-003',
storage: 'Kho Hà Nội',
storage__position: 'WH-HN/B-01',
stock: 220,
preorder: 40,
available: 180,
unit_price: '450000.00',
total: '99000000.00',
batch: 'BATCH-2024-003',
expired: '2025-12-31',
status: 'OK',
moveHistory: [
{
id: 14,
type: 1,
type__name: 'Nhập',
date: '2026-04-01',
code: 'PO-003',
to__code: 'WH-HN/B-01',
delta: 300,
},
{
id: 15,
type: 2,
type__name: 'Xuất',
date: '2026-04-02',
code: 'SO-025',
from__code: 'WH-HN/B-01',
delta: -80,
},
]
},
{
id: 10,
name: 'Son dưỡng môi vitamin E',
category: 'Trang điểm',
sku: 'SD-008',
storage: 'Kho Hà Nội',
storage__position: 'WH-HN/C-01',
stock: 95,
preorder: 45,
available: 50,
unit_price: '75000.00',
total: '7100000.00',
batch: 'BATCH-2024-008',
expired: '2026-02-28',
status: 'OK',
moveHistory: [
{
id: 16,
type: 1,
type__name: 'Nhập',
date: '2026-04-04',
code: 'PO-006',
to__code: 'WH-HN/C-01',
delta: 150,
},
{
id: 17,
type: 2,
type__name: 'Xuất',
date: '2026-04-02',
code: 'SO-030',
from__code: 'WH-HN/C-01',
delta: -55,
},
]
},
{
id: 11,
name: 'Sữa rửa mặt trà xanh 150ml',
category: 'Mỹ phẩm',
sku: 'SRM-002',
storage: 'Kho Hà Nội',
storage__position: 'WH-HN/A-02',
stock: 85,
preorder: 30,
available: 55,
unit_price: '150000.00',
total: '12800000.00',
batch: 'BATCH-2024-002',
expired: '2026-03-20',
status: 'OK',
moveHistory: [
{
id: 18,
type: 1,
type__name: 'Nhập',
date: '2026-04-03',
code: 'PO-002',
to__code: 'WH-HN/A-02',
delta: 100,
},
{
id: 19,
type: 2,
type__name: 'Xuất',
date: '2026-04-04',
code: 'SO-025',
from__code: 'WH-HN/A-02',
delta: -15,
},
]
},
{
id: 12,
name: 'Toner cân bằng da 200ml',
category: 'Dưỡng da',
sku: 'TN-005',
storage: 'Kho TP.HCM',
storage__position: 'WH-HCM/A-02',
stock: 180,
preorder: 25,
available: 155,
unit_price: '180000.00',
total: '32400000.00',
batch: 'BATCH-2024-005',
expired: '2026-06-30',
status: 'OK',
moveHistory: [
{
id: 20,
type: 1,
type__name: 'Nhập',
date: '2026-04-02',
code: 'PO-005',
to__code: 'WH-HCM/A-02',
delta: 200,
},
{
id: 21,
type: 2,
type__name: 'Xuất',
date: '2026-04-04',
code: 'SO-025',
from__code: 'WH-HCM/A-02',
delta: -15,
},
]
},
];
const storages = [
'Kho Hà Nội',
'Kho TP.HCM',
'Kho Đà Nẵng',
];
const categories = [
'Chăm sóc tóc',
'Dưỡng da',
'Mỹ phẩm',
'Mặt nạ',
'Trang điểm',
'Tẩy trang',
];
const statuses = [
'OK',
'Thấp',
];
const input = ref();
const selectedStorage = ref();
const selectedCategory = ref();
const selectedStatus = ref();
const filteredInvItems = computed(() => {
const filteredByInput = invItems.filter(invItem => {
if (!input.value) return true;
const values = Object.values(invItem);
const strValues = values.filter(v => typeof v === 'string');
return strValues.some(str => str.toLowerCase().includes(input.value.toLowerCase()));
});
const filteredByStorage = filteredByInput.filter(invItem => {
if (!selectedStorage.value) return true;
return invItem.storage === selectedStorage.value;
});
const filteredByCategory = filteredByStorage.filter(invItem => {
if (!selectedCategory.value) return true;
return invItem.category === selectedCategory.value;
});
const filteredByStatus = filteredByCategory.filter(invItem => {
if (!selectedStatus.value) return true;
return invItem.status === selectedStatus.value;
});
return filteredByStatus;
})
const selectedInvItem = ref(null);
</script>
<template>
<div class="card">
<div class="card-content">
<div class="is-flex is-gap-2 is-align-items-center">
<div class="field is-flex-grow-1 mb-0">
<p class="control has-icons-left">
<input v-model="input" class="input" type="text" placeholder="Tìm kiếm theo tên, SKU, barcode..." />
<span class="icon is-small is-left">
<Icon name="material-symbols:search-rounded" :size="20" />
</span>
</p>
</div>
<div class="select">
<select v-model="selectedStorage">
<option :value="undefined">Tất cả kho</option>
<option
v-for="storage in storages"
:key="storage"
:value="storage"
>
{{ storage }}
</option>
</select>
</div>
<div class="select">
<select v-model="selectedCategory">
<option :value="undefined">Tất cả danh mục</option>
<option
v-for="category in categories"
:key="category"
:value="category"
>
{{ category }}
</option>
</select>
</div>
<div class="select">
<select v-model="selectedStatus">
<option :value="undefined">Trạng thái tồn</option>
<option
v-for="status in statuses"
:key="status"
:value="status"
>
{{ status }}
</option>
</select>
</div>
</div>
</div>
</div>
<div class="fixed-grid has-3-cols">
<div class="grid">
<div
:class="['cell', selectedInvItem ? 'is-col-span-2' : 'is-col-span-3']"
>
<div class="card is-clipped">
<div class="card-content p-0">
<p class="p-5 fs-17 font-semibold is-flex is-align-items-center is-gap-1">
<Icon name="material-symbols:deployed-code-outline" :size="22" />
<span>Danh sách tồn kho ({{ filteredInvItems.length }})</span>
</p>
<table class="table is-fullwidth is-hoverable fs-13">
<thead>
<tr>
<th class="font-semibold">Sản phẩm</th>
<th class="font-semibold">SKU</th>
<th class="font-semibold">Kho</th>
<th class="font-semibold">Vị trí</th>
<th class="font-semibold has-text-right">Tồn</th>
<th class="font-semibold has-text-right">Đặt trước</th>
<th class="font-semibold has-text-right">Khả dụng</th>
<th class="font-semibold">Batch</th>
<th class="font-semibold">Hạn sử dụng</th>
<th class="font-semibold">Trạng thái</th>
</tr>
</thead>
<tbody>
<InventoryRow
v-for="invItem in filteredInvItems"
:key="invItem.id"
:invItem="invItem"
:selected="invItem.id === selectedInvItem?.id"
@selectInvItem="(id) => {
selectedInvItem = filteredInvItems.find(item => item.id === id);
}"
@unselect="selectedInvItem = null"
/>
</tbody>
</table>
</div>
</div>
</div>
<SelectedInvItem
:invItem="selectedInvItem"
@unselect="selectedInvItem = null"
/>
</div>
</div>
</template>