638 lines
16 KiB
Vue
638 lines
16 KiB
Vue
<script setup>
|
||
import OrderRow from "@/components/orders/OrderRow.vue";
|
||
import SelectedOrder from "@/components/orders/SelectedOrder.vue";
|
||
import { pull } from "es-toolkit";
|
||
|
||
const props = defineProps({
|
||
viewMode: String,
|
||
});
|
||
|
||
const { $dayjs } = useNuxtApp();
|
||
|
||
const orders = [
|
||
{
|
||
id: 1,
|
||
code: "SO001",
|
||
employee: 1,
|
||
employee__name: "Trần Thị B",
|
||
customer: 1,
|
||
customer__name: "Nguyễn Văn A",
|
||
customer__phone: "0901234567",
|
||
total: "5200000.00",
|
||
status: 1,
|
||
status__name: "Nháp",
|
||
status__color: "yellow",
|
||
payment_status: 1,
|
||
payment_status__name: "Chưa thanh toán",
|
||
payment_status__color: "red",
|
||
delivery_status: 1,
|
||
delivery_status__name: "Chờ xử lý",
|
||
delivery_status__color: "grey",
|
||
order__products: [
|
||
{
|
||
id: 1,
|
||
name: "Kem chống nắng SPF 50+",
|
||
unit_price: "280000.00",
|
||
quantity: 10,
|
||
discount: null,
|
||
total: "2800000.00",
|
||
},
|
||
{
|
||
id: 2,
|
||
name: "Serum Vitamin C 30ml",
|
||
unit_price: "450000.00",
|
||
quantity: 5,
|
||
discount: 0.1,
|
||
total: "2025000.00",
|
||
},
|
||
{
|
||
id: 3,
|
||
name: "Son dưỡng môi vitamin E",
|
||
unit_price: "75000.00",
|
||
quantity: 5,
|
||
discount: null,
|
||
total: "375000.00",
|
||
},
|
||
],
|
||
create_time: "2026-02-11T08:51:04.587660+07:00",
|
||
},
|
||
{
|
||
id: 2,
|
||
code: "SO002",
|
||
employee: 1,
|
||
employee__name: "Trần Thị B",
|
||
customer: 3,
|
||
customer__name: "Lê Thị C",
|
||
customer__phone: "0912345678",
|
||
total: "8500000.00",
|
||
status: 2,
|
||
status__name: "Đã xác nhận",
|
||
status__color: "blue",
|
||
payment_status: 2,
|
||
payment_status__name: "Một phần",
|
||
payment_status__color: "yellow",
|
||
delivery_status: 2,
|
||
delivery_status__name: "Sẵn sàng",
|
||
delivery_status__color: "blue",
|
||
order__products: [
|
||
{
|
||
id: 3,
|
||
name: "Mặt nạ collagen 10 miếng",
|
||
unit_price: "320000.00",
|
||
quantity: 15,
|
||
discount: 0.05,
|
||
total: "4560000.00",
|
||
},
|
||
{
|
||
id: 4,
|
||
name: "Toner cân bằng da 200ml",
|
||
unit_price: "180000.00",
|
||
quantity: 20,
|
||
discount: 0.1,
|
||
total: "3240000.00",
|
||
},
|
||
{
|
||
id: 5,
|
||
name: "Gel rửa mặt làm sạch sâu",
|
||
unit_price: "140000.00",
|
||
quantity: 5,
|
||
discount: null,
|
||
total: "700000.00",
|
||
},
|
||
],
|
||
create_time: "2026-04-06T09:10:09.587660+07:00",
|
||
},
|
||
{
|
||
id: 3,
|
||
code: "SO003",
|
||
employee: 5,
|
||
employee__name: "Hoàng Văn E",
|
||
customer: 4,
|
||
customer__name: "Phạm Văn D",
|
||
customer__phone: "0923456703",
|
||
total: "12300000.00",
|
||
status: 3,
|
||
status__name: "Đang giao",
|
||
status__color: "orange",
|
||
payment_status: 3,
|
||
payment_status__name: "Đã thanh toán",
|
||
payment_status__color: "green",
|
||
delivery_status: 3,
|
||
delivery_status__name: "Hoàn thành",
|
||
delivery_status__color: "green",
|
||
order__products: [
|
||
{
|
||
id: 6,
|
||
name: "Kem dưỡng ẩm ban đêm",
|
||
unit_price: "380000.00",
|
||
quantity: 20,
|
||
discount: 0.05,
|
||
total: "7220000.00",
|
||
},
|
||
{
|
||
id: 7,
|
||
name: "Phấn nền BB cream",
|
||
unit_price: "220000.00",
|
||
quantity: 15,
|
||
discount: null,
|
||
total: "3300000.00",
|
||
},
|
||
{
|
||
id: 8,
|
||
name: "Nước tẩy trang 3 trong 1",
|
||
unit_price: "195000.00",
|
||
quantity: 10,
|
||
discount: 0.1,
|
||
total: "1755000.00",
|
||
},
|
||
],
|
||
create_time: "2026-04-05T02:33:24.587660+07:00",
|
||
},
|
||
{
|
||
id: 4,
|
||
code: "SO004",
|
||
employee: 1,
|
||
employee__name: "Trần Thị B",
|
||
customer: 6,
|
||
customer__name: "Vũ Thị F",
|
||
customer__phone: "0934835222",
|
||
total: "6800000.00",
|
||
status: 4,
|
||
status__name: "Hoàn thành",
|
||
status__color: "green",
|
||
payment_status: 3,
|
||
payment_status__name: "Đã thanh toán",
|
||
payment_status__color: "green",
|
||
delivery_status: 3,
|
||
delivery_status__name: "Hoàn thành",
|
||
delivery_status__color: "green",
|
||
order__products: [
|
||
{
|
||
id: 9,
|
||
name: "Dầu gội thảo mộc 500ml",
|
||
unit_price: "120000.00",
|
||
quantity: 30,
|
||
discount: 0.1,
|
||
total: "3240000.00",
|
||
},
|
||
{
|
||
id: 10,
|
||
name: "Sữa rửa mặt trà xanh 150ml",
|
||
unit_price: "150000.00",
|
||
quantity: 20,
|
||
discount: 0.05,
|
||
total: "2850000.00",
|
||
},
|
||
{
|
||
id: 11,
|
||
name: "Son dưỡng môi vitamin E",
|
||
unit_price: "75000.00",
|
||
quantity: 10,
|
||
discount: 0.05,
|
||
total: "712500.00",
|
||
},
|
||
],
|
||
create_time: "2026-04-04T23:21:11.587660+07:00",
|
||
},
|
||
{
|
||
id: 5,
|
||
code: "SO005",
|
||
employee: 5,
|
||
employee__name: "Hoàng Văn E",
|
||
customer: 7,
|
||
customer__name: "Đỗ Văn G",
|
||
customer__phone: "0945781113",
|
||
total: "9400000.00",
|
||
status: 2,
|
||
status__name: "Đã xác nhận",
|
||
status__color: "blue",
|
||
payment_status: 1,
|
||
payment_status__name: "Chưa thanh toán",
|
||
payment_status__color: "red",
|
||
delivery_status: 1,
|
||
delivery_status__name: "Chờ xử lý",
|
||
delivery_status__color: "grey",
|
||
order__products: [
|
||
{
|
||
id: 12,
|
||
name: "Kem mắt chống lão hóa",
|
||
unit_price: "550000.00",
|
||
quantity: 10,
|
||
discount: null,
|
||
total: "5500000.00",
|
||
},
|
||
{
|
||
id: 13,
|
||
name: "Serum Vitamin C 30ml",
|
||
unit_price: "450000.00",
|
||
quantity: 8,
|
||
discount: 0.05,
|
||
total: "3420000.00",
|
||
},
|
||
{
|
||
id: 14,
|
||
name: "Gel rửa mặt làm sạch sâu",
|
||
unit_price: "140000.00",
|
||
quantity: 5,
|
||
discount: 0.15,
|
||
total: "595000.00",
|
||
},
|
||
],
|
||
create_time: "2026-04-07T11:21:46.587660+07:00",
|
||
},
|
||
{
|
||
id: 6,
|
||
code: "SO006",
|
||
employee: 1,
|
||
employee__name: "Trần Thị B",
|
||
customer: 8,
|
||
customer__name: "Bùi Thị H",
|
||
customer__phone: "0933184392",
|
||
total: "4200000.00",
|
||
status: 1,
|
||
status__name: "Nháp",
|
||
status__color: "yellow",
|
||
payment_status: 1,
|
||
payment_status__name: "Chưa thanh toán",
|
||
payment_status__color: "red",
|
||
delivery_status: 1,
|
||
delivery_status__name: "Chờ xử lý",
|
||
delivery_status__color: "grey",
|
||
order__products: [
|
||
{
|
||
id: 15,
|
||
name: "Toner cân bằng da 200ml",
|
||
unit_price: "180000.00",
|
||
quantity: 15,
|
||
discount: null,
|
||
total: "2700000.00",
|
||
},
|
||
{
|
||
id: 16,
|
||
name: "Mặt nạ collagen 10 miếng",
|
||
unit_price: "320000.00",
|
||
quantity: 5,
|
||
discount: 0.05,
|
||
total: "1520000.00",
|
||
},
|
||
],
|
||
create_time: "2026-04-07T13:17:36.587660+07:00",
|
||
},
|
||
{
|
||
id: 7,
|
||
code: "SO007",
|
||
employee: 5,
|
||
employee__name: "Hoàng Văn E",
|
||
customer: 9,
|
||
customer__name: "Ngô Văn I",
|
||
customer__phone: "0978335172",
|
||
total: "15600000.00",
|
||
status: 3,
|
||
status__name: "Đang giao",
|
||
status__color: "orange",
|
||
payment_status: 3,
|
||
payment_status__name: "Đã thanh toán",
|
||
payment_status__color: "green",
|
||
delivery_status: 2,
|
||
delivery_status__name: "Hoàn thành",
|
||
delivery_status__color: "green",
|
||
order__products: [
|
||
{
|
||
id: 17,
|
||
name: "Kem chống nắng SPF 50+",
|
||
unit_price: "280000.00",
|
||
quantity: 30,
|
||
discount: 0.1,
|
||
total: "7560000.00",
|
||
},
|
||
{
|
||
id: 18,
|
||
name: "Phấn nền BB cream",
|
||
unit_price: "220000.00",
|
||
quantity: 25,
|
||
discount: 0.05,
|
||
total: "5225000.00",
|
||
},
|
||
{
|
||
id: 19,
|
||
name: "Kem dưỡng ẩm ban đêm",
|
||
unit_price: "380000.00",
|
||
quantity: 8,
|
||
discount: 0.05,
|
||
total: "2880000.00",
|
||
},
|
||
],
|
||
create_time: "2026-06-04T09:30:12.587660+07:00",
|
||
},
|
||
{
|
||
id: 8,
|
||
code: "SO008",
|
||
employee: 1,
|
||
employee__name: "Trần Thị B",
|
||
customer: 11,
|
||
customer__name: "Đinh Thị K",
|
||
customer__phone: "0922104853",
|
||
total: "7900000.00",
|
||
status: 2,
|
||
status__name: "Đã xác nhận",
|
||
status__color: "blue",
|
||
payment_status: 2,
|
||
payment_status__name: "Một phần",
|
||
payment_status__color: "yellow",
|
||
delivery_status: 2,
|
||
delivery_status__name: "Sẵn sàng",
|
||
delivery_status__color: "blue",
|
||
order__products: [
|
||
{
|
||
id: 20,
|
||
name: "Nước tẩy trang 3 trong 1",
|
||
unit_price: "195000.00",
|
||
quantity: 20,
|
||
discount: 0.05,
|
||
total: "3705000.00",
|
||
},
|
||
{
|
||
id: 21,
|
||
name: "Sữa rửa mặt trà xanh 150ml",
|
||
unit_price: "150000.00",
|
||
quantity: 18,
|
||
discount: null,
|
||
total: "2700000.00",
|
||
},
|
||
{
|
||
id: 22,
|
||
name: "Dầu gội thảo mộc 500ml",
|
||
unit_price: "120000.00",
|
||
quantity: 13,
|
||
discount: 0.05,
|
||
total: "1482000.00",
|
||
},
|
||
],
|
||
create_time: "2026-04-06T16:01:22.587660+07:00",
|
||
},
|
||
];
|
||
|
||
const statuses = [
|
||
{
|
||
id: 1,
|
||
name: "Nháp",
|
||
color: "yellow",
|
||
},
|
||
{
|
||
id: 2,
|
||
name: "Đã xác nhận",
|
||
color: "blue",
|
||
},
|
||
{
|
||
id: 3,
|
||
name: "Đang giao",
|
||
color: "orange",
|
||
},
|
||
{
|
||
id: 4,
|
||
name: "Hoàn thành",
|
||
color: "green",
|
||
},
|
||
];
|
||
|
||
const paymentStatuses = [
|
||
{
|
||
id: 1,
|
||
name: "Chưa thanh toán",
|
||
color: "red",
|
||
},
|
||
{
|
||
id: 2,
|
||
name: "Một phần",
|
||
color: "yellow",
|
||
},
|
||
{
|
||
id: 3,
|
||
name: "Đã thanh toán",
|
||
color: "green",
|
||
},
|
||
];
|
||
|
||
const employees = [
|
||
{
|
||
id: 1,
|
||
name: "Trần Thị B",
|
||
},
|
||
{
|
||
id: 5,
|
||
name: "Hoàng Văn E",
|
||
},
|
||
];
|
||
|
||
const input = ref();
|
||
const dateRange = ref({
|
||
from: null,
|
||
to: null,
|
||
});
|
||
|
||
const selectedStatuses = ref([]);
|
||
const selectedPaymentStatus = ref();
|
||
const selectedEmployee = ref();
|
||
|
||
const filteredOrders = computed(() => {
|
||
const filteredByInput = orders.filter((order) => {
|
||
if (!input.value) return true;
|
||
const values = Object.values(order);
|
||
const strValues = values.filter((v) => typeof v === "string");
|
||
return strValues.some((str) => str.toLowerCase().includes(input.value.toLowerCase()));
|
||
});
|
||
|
||
const filteredByStatuses = filteredByInput.filter((order) => {
|
||
if (selectedStatuses.value.length === 0) return true;
|
||
return selectedStatuses.value.includes(order.status);
|
||
});
|
||
|
||
const filteredByPaymentStatuses = filteredByStatuses.filter((order) => {
|
||
if (!selectedPaymentStatus.value) return true;
|
||
return order.payment_status === selectedPaymentStatus.value;
|
||
});
|
||
|
||
const filteredByEmployees = filteredByPaymentStatuses.filter((order) => {
|
||
if (!selectedEmployee.value) return true;
|
||
return order.employee === selectedEmployee.value;
|
||
});
|
||
|
||
const filteredByDates = filteredByEmployees.filter((order) => {
|
||
if (!dateRange.value) return true;
|
||
const from = $dayjs(dateRange.value.from || 0);
|
||
const to = $dayjs(dateRange.value.to || undefined);
|
||
const createTime = $dayjs(order.create_time);
|
||
return createTime.isSameOrAfter(from) && createTime.isSameOrBefore(to);
|
||
});
|
||
|
||
return filteredByDates;
|
||
});
|
||
|
||
const selectedOrder = ref(null);
|
||
|
||
watch(filteredOrders, () => {
|
||
selectedOrder.value = null;
|
||
});
|
||
|
||
function toggleStatus(id) {
|
||
if (selectedStatuses.value.includes(id)) {
|
||
selectedStatuses.value = pull(selectedStatuses.value, [id]);
|
||
} else {
|
||
selectedStatuses.value.push(id);
|
||
}
|
||
}
|
||
</script>
|
||
<template>
|
||
<div>
|
||
<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 m-0">
|
||
<p class="control has-icons-left">
|
||
<input
|
||
v-model="input"
|
||
class="input"
|
||
type="text"
|
||
placeholder="Tìm kiếm mã đơn, khách hàng..."
|
||
/>
|
||
<span class="icon is-small is-left">
|
||
<Icon
|
||
name="material-symbols:search-rounded"
|
||
:size="24"
|
||
/>
|
||
</span>
|
||
</p>
|
||
</div>
|
||
<div class="is-flex is-gap-1 is-align-items-center">
|
||
<!-- Date pickers -->
|
||
<Datepicker
|
||
v-bind="{
|
||
record: dateRange,
|
||
attr: 'from',
|
||
maxdate: new Date(),
|
||
onDate: (e) => (dateRange.from = e),
|
||
}"
|
||
/>
|
||
<span>–</span>
|
||
<Datepicker
|
||
v-bind="{
|
||
record: dateRange,
|
||
attr: 'to',
|
||
maxdate: new Date(),
|
||
onDate: (e) => (dateRange.to = e),
|
||
}"
|
||
/>
|
||
</div>
|
||
<div class="select">
|
||
<select v-model="selectedPaymentStatus">
|
||
<option :value="undefined">Phương thức thanh toán</option>
|
||
<option
|
||
v-for="paymentStatus in paymentStatuses"
|
||
:key="paymentStatus.id"
|
||
:value="paymentStatus.id"
|
||
>
|
||
{{ paymentStatus.name }}
|
||
</option>
|
||
</select>
|
||
</div>
|
||
<div class="select">
|
||
<select v-model="selectedEmployee">
|
||
<option :value="undefined">Nhân viên</option>
|
||
<option
|
||
v-for="employee in employees"
|
||
:key="employee.id"
|
||
:value="employee.id"
|
||
>
|
||
{{ employee.name }}
|
||
</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="is-flex is-gap-1 mt-3">
|
||
<button
|
||
v-for="status in statuses"
|
||
:key="status.id"
|
||
:class="[
|
||
'tag fs-13 is-rounded',
|
||
selectedStatuses.includes(status.id) && `has-background-${status.color}-80`,
|
||
]"
|
||
@click="toggleStatus(status.id)"
|
||
>
|
||
{{ status.name }}
|
||
<Icon
|
||
v-if="selectedStatuses.includes(status.id)"
|
||
name="material-symbols:check-rounded"
|
||
:size="18"
|
||
class="ml-1"
|
||
/>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="fixed-grid has-3-cols">
|
||
<div class="grid">
|
||
<div :class="['cell', selectedOrder ? 'is-col-span-2' : 'is-col-span-3']">
|
||
<div class="card is-clipped">
|
||
<div class="card-content p-0">
|
||
<template v-if="viewMode === 'list'">
|
||
<p class="p-5 fs-17 font-semibold is-flex is-align-items-center is-gap-1">
|
||
<Icon
|
||
name="material-symbols:list-alt-outline-rounded"
|
||
:size="22"
|
||
/>
|
||
<span>Danh sách đơn hàng ({{ filteredOrders.length }})</span>
|
||
</p>
|
||
<table class="table is-fullwidth is-hoverable fs-13">
|
||
<thead>
|
||
<tr>
|
||
<th class="font-semibold">Đơn hàng</th>
|
||
<th class="font-semibold">Khách hàng</th>
|
||
<th class="font-semibold has-text-right">Tổng tiền</th>
|
||
<th class="font-semibold has-text-centered">Trạng thái</th>
|
||
<th class="font-semibold">Thanh toán</th>
|
||
<th class="font-semibold">Giao hàng</th>
|
||
<th class="font-semibold">Ngày tạo</th>
|
||
<th class="font-semibold">Thao tác</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<OrderRow
|
||
v-for="order in filteredOrders"
|
||
:key="order.id"
|
||
v-bind="{
|
||
order,
|
||
selected: order.id === selectedOrder?.id,
|
||
}"
|
||
@selectOrder="
|
||
(id) => {
|
||
selectedOrder = filteredOrders.find((order) => order.id === id);
|
||
}
|
||
"
|
||
@unselect="selectedOrder = null"
|
||
/>
|
||
</tbody>
|
||
</table>
|
||
</template>
|
||
<OrdersKanban
|
||
v-else
|
||
:orders="filteredOrders"
|
||
:statuses="statuses"
|
||
@selectOrder="
|
||
(id) => {
|
||
selectedOrder = filteredOrders.find((order) => order.id === id);
|
||
}
|
||
"
|
||
@unselect="selectedOrder = null"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<SelectedOrder
|
||
:order="selectedOrder"
|
||
@unselect="selectedOrder = null"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|