Files
web/app/components/inventory/InventoryTable.vue
2026-05-05 11:06:49 +07:00

564 lines
14 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>