changes
This commit is contained in:
118
app/components/orders/InvoiceRow.vue
Normal file
118
app/components/orders/InvoiceRow.vue
Normal file
@@ -0,0 +1,118 @@
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
invoice: Object,
|
||||
});
|
||||
|
||||
const { $dayjs, $shortenCurrency } = useNuxtApp();
|
||||
const showModal = ref();
|
||||
|
||||
function openModal() {
|
||||
showModal.value = {
|
||||
component: "orders/SelectedOrder",
|
||||
title: "Chi tiết đơn hàng",
|
||||
width: "75%",
|
||||
height: "500px",
|
||||
vbind: {
|
||||
invoice: props.invoice,
|
||||
},
|
||||
};
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<tr
|
||||
class="is-clickable"
|
||||
@click="openModal"
|
||||
>
|
||||
<td>
|
||||
<div>
|
||||
<p class="fs-15 font-semibold">{{ invoice.code }}</p>
|
||||
<p class="has-text-grey">{{ invoice.staff__fullname }}</p>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div>
|
||||
<p>{{ invoice.customer_name }}</p>
|
||||
<div class="is-flex is-gap-0.5 is-align-items-center mt-1 has-text-grey">
|
||||
<Icon
|
||||
name="material-symbols:call-outline-rounded"
|
||||
:size="15"
|
||||
/>
|
||||
<span class="fs-12">{{ invoice.customer_phone }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="has-text-right">
|
||||
<div>
|
||||
<p class="fs-14 font-semibold">{{ $shortenCurrency(invoice.total_amount) }}</p>
|
||||
<p class="has-text-grey">{{ invoice.product_amount }} SP</p>
|
||||
</div>
|
||||
</td>
|
||||
<td class="has-text-centered">
|
||||
<span
|
||||
:class="['tag rounded-full', `has-background-${invoice.status__color}-80 has-text-${invoice.status__color}-25`]"
|
||||
>
|
||||
{{ invoice.status }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<p :class="`has-text-${invoice.payment_status__color}-40`">
|
||||
{{ invoice.payment_status__name }}
|
||||
</p>
|
||||
</td>
|
||||
<td>
|
||||
<p :class="`has-text-${invoice.delivery_status__color}-40`">
|
||||
{{ invoice.delivery__status }}
|
||||
</p>
|
||||
</td>
|
||||
<td>
|
||||
<div>
|
||||
<div class="is-flex is-gap-0.5">
|
||||
<Icon
|
||||
:size="18"
|
||||
name="material-symbols:calendar-today-outline-rounded"
|
||||
/>
|
||||
<span>{{ $dayjs(invoice.create_time).format("L") }}</span>
|
||||
</div>
|
||||
<p class="has-text-grey fs-12">
|
||||
{{ $dayjs(invoice.create_time).format("HH:mm") }}
|
||||
</p>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
v-if="invoice.status__name !== 'Hoàn thành'"
|
||||
:class="[
|
||||
'button fs-12 has-text-white rounded-lg',
|
||||
invoice.status__name === 'Nháp'
|
||||
? 'is-primary'
|
||||
: invoice.status__name === 'Đã xác nhận'
|
||||
? 'is-orange'
|
||||
: 'is-success',
|
||||
]"
|
||||
>
|
||||
{{
|
||||
invoice.status__name === "Nháp"
|
||||
? "Xác nhận"
|
||||
: invoice.status__name === "Đã xác nhận"
|
||||
? "Giao hàng"
|
||||
: "Hoàn thành"
|
||||
}}
|
||||
</button>
|
||||
</td>
|
||||
<Modal
|
||||
v-if="showModal"
|
||||
v-bind="showModal"
|
||||
@close="showModal = null"
|
||||
/>
|
||||
</tr>
|
||||
</template>
|
||||
<style scoped>
|
||||
tr.is-selected {
|
||||
--bulma-table-row-active-background-color: var(--bulma-primary-95);
|
||||
color: unset;
|
||||
}
|
||||
td {
|
||||
vertical-align: middle;
|
||||
--bulma-table-cell-padding: 0.75em;
|
||||
}
|
||||
</style>
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
order: Object,
|
||||
invoice: Object,
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -9,13 +9,13 @@ const props = defineProps({
|
||||
<div class="p-4 rounded-md has-background-primary-95">
|
||||
<p class="has-text-grey">Trạng thái</p>
|
||||
<p class="fs-17 mt-1 font-semibold has-text-primary-50">
|
||||
{{ order.delivery_status__name }}
|
||||
{{ invoice.delivery_status__name }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="p-4 rounded-md has-background-white-bis">
|
||||
<p class="has-text-grey">Địa chỉ giao hàng</p>
|
||||
<p class="mt-1 has-text-grey-darker">{{ order.customer__name }}</p>
|
||||
<p class="has-text-grey-darker">{{ order.customer__phone }}</p>
|
||||
<p class="mt-1 has-text-grey-darker">{{ invoice.customer__name }}</p>
|
||||
<p class="has-text-grey-darker">{{ invoice.customer__phone }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
order: Object,
|
||||
invoice: Object,
|
||||
});
|
||||
|
||||
const { $dayjs } = useNuxtApp();
|
||||
|
||||
@@ -5,6 +5,19 @@ const props = defineProps({
|
||||
|
||||
const { $dayjs, $shortenCurrency } = useNuxtApp();
|
||||
const emit = defineEmits(["selectOrder", "unselect"]);
|
||||
const showModal = ref();
|
||||
|
||||
function openModal() {
|
||||
showModal.value = {
|
||||
component: "orders/SelectedOrder",
|
||||
title: "SelectedOrder",
|
||||
width: "75%",
|
||||
height: "500px",
|
||||
vbind: {
|
||||
order: props.order,
|
||||
},
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -72,5 +85,10 @@ const emit = defineEmits(["selectOrder", "unselect"]);
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<Modal
|
||||
v-if="showModal"
|
||||
v-bind="showModal"
|
||||
@close="showModal = null"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
order: Object,
|
||||
invoice: Object,
|
||||
});
|
||||
const { $numtoString } = useNuxtApp();
|
||||
</script>
|
||||
@@ -11,7 +11,7 @@ const { $numtoString } = useNuxtApp();
|
||||
<div class="is-flex-grow-1 p-3 rounded-md has-background-white-bis">
|
||||
<p class="fs-13 has-text-grey">Tổng tiền</p>
|
||||
<p class="font-bold">
|
||||
{{ $numtoString(order.total, { hasUnit: true }) }}
|
||||
{{ $numtoString(invoice.total, { hasUnit: true }) }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="is-flex-grow-1 p-3 rounded-md has-background-success-95">
|
||||
@@ -22,7 +22,7 @@ const { $numtoString } = useNuxtApp();
|
||||
<div class="p-4 mt-4 rounded-md has-background-danger-95">
|
||||
<p class="fs-13 has-text-danger-50">Còn lại</p>
|
||||
<p class="fs-20 font-bold has-text-danger-40">
|
||||
{{ $numtoString(order.total, { hasUnit: true }) }}
|
||||
{{ $numtoString(invoice.total, { hasUnit: true }) }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,39 +1,66 @@
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
order: Object,
|
||||
invoice: Object,
|
||||
});
|
||||
|
||||
const { $numtoString } = useNuxtApp();
|
||||
const { $getdata, $numtoString } = useNuxtApp();
|
||||
const invoiceDetails = ref([]);
|
||||
onMounted(async () => {
|
||||
invoiceDetails.value = await $getdata("Invoice_Detail", { filter: { invoice: props.invoice.id } });
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<pre class="fs-12 max-h-40 mb-2">{{ invoiceDetails }}</pre>
|
||||
<div class="is-flex is-flex-direction-column is-gap-2">
|
||||
<div
|
||||
v-for="product in order.order__products"
|
||||
:key="product.id"
|
||||
v-for="invoiceDetail in invoiceDetails"
|
||||
:key="invoiceDetail.id"
|
||||
class="p-3 has-background-white-ter rounded-md"
|
||||
>
|
||||
<div class="fs-15 is-flex is-justify-content-space-between">
|
||||
<p class="">{{ product.name }}</p>
|
||||
<p class="font-bold">
|
||||
{{ $numtoString(product.total, { hasUnit: true }) }}
|
||||
<p>{{ invoiceDetail.variant__product__name }}</p>
|
||||
<p class="font-semibold">
|
||||
{{ $numtoString(invoiceDetail.price, { hasUnit: true }) }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="is-flex is-gap-8 fs-13 has-text-grey-dark mt-1">
|
||||
<p>SL: {{ product.quantity }}</p>
|
||||
<p>Đơn giá: {{ $numtoString(product.unit_price, { hasUnit: true }) }}</p>
|
||||
<p>SL: {{ invoiceDetail.quantity }}</p>
|
||||
<p>Đơn giá: {{ $numtoString(invoiceDetail.price, { hasUnit: true }) }}</p>
|
||||
<p>
|
||||
Giảm:
|
||||
{{ new Intl.NumberFormat("vi-VN", { style: "percent" }).format(product.discount) }}
|
||||
{{ new Intl.NumberFormat("vi-VN", { style: "percent" }).format(invoiceDetail.discount) }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<div class="font-bold is-flex is-gap-2 is-justify-content-space-between is-align-items-center">
|
||||
<p class="fs-15">Tổng cộng</p>
|
||||
<p class="fs-18">{{ $numtoString(order.total, { hasUnit: true }) }}</p>
|
||||
</div>
|
||||
<table class="table is-fullwidth is-bordered fs-14 mb-8">
|
||||
<tbody class="has-text-right">
|
||||
<tr>
|
||||
<td class="has-text-grey-dark">Tổng tiền hàng</td>
|
||||
<td>{{ $numtoString(invoice.total_amount, { hasUnit: true }) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="has-text-grey-dark">Phí vận chuyển</td>
|
||||
<td>{{ $numtoString(invoice.shipping_fee, { hasUnit: true }) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="has-text-grey-dark">Giảm giá</td>
|
||||
<td>{{ $numtoString(invoice.discount_amount, { hasUnit: true }) }}</td>
|
||||
</tr>
|
||||
<tr class="font-bold">
|
||||
<td>Tổng cộng</td>
|
||||
<td class="fs-17">{{ $numtoString(invoice.final_amount, { hasUnit: true }) }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
table td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
order: Object,
|
||||
invoice: Object,
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
order: Object,
|
||||
selected: Boolean,
|
||||
});
|
||||
|
||||
const { $dayjs, $shortenCurrency } = useNuxtApp();
|
||||
const emit = defineEmits(["selectOrder", "unselect"]);
|
||||
</script>
|
||||
<template>
|
||||
<tr
|
||||
:class="['is-clickable', selected && 'is-selected']"
|
||||
@click="selected ? emit('unselect') : emit('selectOrder', order.id)"
|
||||
>
|
||||
<td>
|
||||
<div>
|
||||
<p class="fs-15 font-semibold">{{ order.code }}</p>
|
||||
<p class="has-text-grey">{{ order.employee__name }}</p>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div>
|
||||
<p>{{ order.customer__name }}</p>
|
||||
<div class="is-flex is-gap-0.5 is-align-items-center mt-1 has-text-grey">
|
||||
<Icon
|
||||
name="material-symbols:call-outline-rounded"
|
||||
:size="15"
|
||||
/>
|
||||
<span class="fs-12">{{ order.customer__phone }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="has-text-right">
|
||||
<div>
|
||||
<p class="fs-14 font-semibold">{{ $shortenCurrency(order.total) }}</p>
|
||||
<p class="has-text-grey">{{ order.order__products.length }} SP</p>
|
||||
</div>
|
||||
</td>
|
||||
<td class="has-text-centered">
|
||||
<span
|
||||
:class="['tag rounded-full', `has-background-${order.status__color}-80 has-text-${order.status__color}-25`]"
|
||||
>
|
||||
{{ order.status__name }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<p :class="`has-text-${order.payment_status__color}-40`">
|
||||
{{ order.payment_status__name }}
|
||||
</p>
|
||||
</td>
|
||||
<td>
|
||||
<p :class="`has-text-${order.delivery_status__color}-40`">
|
||||
{{ order.delivery_status__name }}
|
||||
</p>
|
||||
</td>
|
||||
<td>
|
||||
<div>
|
||||
<div class="is-flex is-gap-0.5">
|
||||
<Icon
|
||||
:size="18"
|
||||
name="material-symbols:calendar-today-outline-rounded"
|
||||
/>
|
||||
<span>{{ $dayjs(order.create_time).format("L") }}</span>
|
||||
</div>
|
||||
<p class="has-text-grey fs-12">
|
||||
{{ $dayjs(order.create_time).format("HH:mm") }}
|
||||
</p>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
v-if="order.status__name !== 'Hoàn thành'"
|
||||
:class="[
|
||||
'button fs-12 has-text-white rounded-lg',
|
||||
order.status__name === 'Nháp'
|
||||
? 'is-primary'
|
||||
: order.status__name === 'Đã xác nhận'
|
||||
? 'is-orange'
|
||||
: 'is-success',
|
||||
]"
|
||||
>
|
||||
{{
|
||||
order.status__name === "Nháp" ? "Xác nhận" : order.status__name === "Đã xác nhận" ? "Giao hàng" : "Hoàn thành"
|
||||
}}
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
<style scoped>
|
||||
tr.is-selected {
|
||||
--bulma-table-row-active-background-color: var(--bulma-primary-95);
|
||||
color: unset;
|
||||
}
|
||||
td {
|
||||
vertical-align: middle;
|
||||
--bulma-table-cell-padding: 0.75em;
|
||||
}
|
||||
</style>
|
||||
@@ -3,7 +3,7 @@ import OrderHighlightCard from "~/components/orders/OrderHighlightCard.vue";
|
||||
import OrderPipeline from "~/components/orders/OrderPipeline.vue";
|
||||
import OrdersTable from "~/components/orders/OrdersTable.vue";
|
||||
|
||||
const { $store } = useNuxtApp();
|
||||
const { $getdata, $store } = useNuxtApp();
|
||||
const highlights = [
|
||||
{
|
||||
name: "Nháp",
|
||||
@@ -43,6 +43,14 @@ const viewModes = [
|
||||
{ name: "grid", icon: "material-symbols:grid-view-outline-rounded" },
|
||||
];
|
||||
const viewMode = ref("list");
|
||||
|
||||
const invoices = ref();
|
||||
onMounted(async () => {
|
||||
invoices.value = await $getdata("Invoice");
|
||||
});
|
||||
provide("orders", {
|
||||
invoices,
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
@@ -82,7 +90,7 @@ const viewMode = ref("list");
|
||||
</button>
|
||||
</div>
|
||||
</Teleport>
|
||||
<div class="fixed-grid has-2-cols-mobile has-5-cols">
|
||||
<!-- <div class="fixed-grid has-2-cols-mobile has-5-cols">
|
||||
<div class="grid">
|
||||
<OrderHighlightCard
|
||||
v-for="highlight in highlights"
|
||||
@@ -91,7 +99,7 @@ const viewMode = ref("list");
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<OrderPipeline />
|
||||
<OrderPipeline /> -->
|
||||
<OrdersTable :viewMode="viewMode" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,376 +1,14 @@
|
||||
<script setup>
|
||||
import OrderRow from "~/components/orders/OrderRow.vue";
|
||||
import SelectedOrder from "~/components/orders/SelectedOrder.vue";
|
||||
import { pull } from "es-toolkit";
|
||||
import InvoiceRow from "~/components/orders/InvoiceRow.vue";
|
||||
import OrdersKanban from "~/components/orders/OrdersKanban.vue";
|
||||
|
||||
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 { invoices } = inject("orders");
|
||||
|
||||
const statuses = [
|
||||
{
|
||||
@@ -434,8 +72,9 @@ const selectedStatuses = ref([]);
|
||||
const selectedPaymentStatus = ref();
|
||||
const selectedEmployee = ref();
|
||||
|
||||
const filteredOrders = computed(() => {
|
||||
const filteredByInput = orders.filter((order) => {
|
||||
const filteredInvoices = computed(() => {
|
||||
if (!invoices.value) return [];
|
||||
const filteredByInput = invoices.value.filter((order) => {
|
||||
if (!input.value) return true;
|
||||
const values = Object.values(order);
|
||||
const strValues = values.filter((v) => typeof v === "string");
|
||||
@@ -470,7 +109,7 @@ const filteredOrders = computed(() => {
|
||||
|
||||
const selectedOrder = ref(null);
|
||||
|
||||
watch(filteredOrders, () => {
|
||||
watch(filteredInvoices, () => {
|
||||
selectedOrder.value = null;
|
||||
});
|
||||
|
||||
@@ -484,7 +123,10 @@ function toggleStatus(id) {
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<div class="card">
|
||||
<div
|
||||
v-if="true"
|
||||
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">
|
||||
@@ -569,69 +211,45 @@ function toggleStatus(id) {
|
||||
</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 icon-text fs-16 font-semibold w-full">
|
||||
<span class="icon">
|
||||
<Icon
|
||||
name="material-symbols:list-alt-outline-rounded"
|
||||
:size="22"
|
||||
/>
|
||||
</span>
|
||||
<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 class="card is-clipped">
|
||||
<div class="card-content p-0">
|
||||
<p class="p-5 icon-text fs-16 font-semibold w-full">
|
||||
<span class="icon">
|
||||
<Icon
|
||||
name="material-symbols:list-alt-outline-rounded"
|
||||
:size="22"
|
||||
/>
|
||||
</span>
|
||||
<span>Danh sách đơn hàng ({{ filteredInvoices.length }})</span>
|
||||
</p>
|
||||
<table
|
||||
v-if="viewMode === 'list'"
|
||||
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>
|
||||
<InvoiceRow
|
||||
v-for="invoice in filteredInvoices"
|
||||
:key="invoice.id"
|
||||
:invoice="invoice"
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
<OrdersKanban
|
||||
v-else
|
||||
:orders="filteredInvoices"
|
||||
:statuses="statuses"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -6,7 +6,7 @@ import OrderProductTab from "~/components/orders/OrderProductTab.vue";
|
||||
import OrderReceiptTab from "~/components/orders/OrderReceiptTab.vue";
|
||||
|
||||
const props = defineProps({
|
||||
order: Object,
|
||||
invoice: Object,
|
||||
});
|
||||
|
||||
const { $dayjs, $numtoString } = useNuxtApp();
|
||||
@@ -44,131 +44,112 @@ const activeTab = ref(tabs[0]);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
v-if="order"
|
||||
class="cell relative fs-14"
|
||||
>
|
||||
<div class="card">
|
||||
<button
|
||||
@click="emit('unselect')"
|
||||
class="button is-white rounded-full has-text-grey absolute size-8 is-flex is-justify-content-center is-align-items-center"
|
||||
style="right: 0.5rem; top: 0.5rem"
|
||||
>
|
||||
<span class="icon">
|
||||
<Icon
|
||||
name="material-symbols:close-rounded"
|
||||
:size="22"
|
||||
/>
|
||||
<div v-if="invoice">
|
||||
<div>
|
||||
<div class="is-flex is-gap-1.5 is-align-items-center">
|
||||
<span class="fs-17 font-bold">{{ invoice.code }}</span>
|
||||
<span
|
||||
:class="[
|
||||
'tag rounded-full',
|
||||
`has-background-${invoice.status__color}-80 has-text-${invoice.status__color}-25`,
|
||||
]"
|
||||
>
|
||||
{{ invoice.status }}
|
||||
</span>
|
||||
</button>
|
||||
<div class="card-content">
|
||||
<div>
|
||||
<div class="is-flex is-gap-2 is-align-items-center">
|
||||
<span class="fs-17 font-bold">{{ order.code }}</span>
|
||||
<span
|
||||
:class="[
|
||||
'tag rounded-full',
|
||||
`has-background-${order.status__color}-80 has-text-${order.status__color}-25`,
|
||||
]"
|
||||
>
|
||||
{{ order.status__name }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="is-flex is-gap-0.5 is-flex-direction-column mt-2">
|
||||
<div class="is-flex is-gap-1 is-align-items-center">
|
||||
<Icon
|
||||
name="material-symbols:person-outline-rounded"
|
||||
:size="18"
|
||||
/>
|
||||
<p>{{ order.customer__name }}</p>
|
||||
</div>
|
||||
<div class="is-flex is-gap-1 is-align-items-center">
|
||||
<Icon
|
||||
name="material-symbols:call-outline-rounded"
|
||||
:size="18"
|
||||
/>
|
||||
<p>{{ order.customer__phone }}</p>
|
||||
</div>
|
||||
<div class="is-flex is-gap-1 is-align-items-center">
|
||||
<Icon
|
||||
name="material-symbols:calendar-today-outline-rounded"
|
||||
:size="18"
|
||||
/>
|
||||
<p>{{ $dayjs(order.create_time).format("LL") }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-4 has-background-primary-95 rounded-lg mt-6">
|
||||
<p>Tổng giá trị đơn hàng</p>
|
||||
<p class="font-bold fs-28">
|
||||
{{ $numtoString(order.total, { hasUnit: true }) }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="m-0" />
|
||||
<div class="buttons m-0">
|
||||
<button class="button fs-14 is-flex-grow-1 is-primary">Xác nhận đơn</button>
|
||||
<button class="button fs-14 is-flex-grow-1">Ghi thanh toán</button>
|
||||
</div>
|
||||
<hr class="m-0" />
|
||||
<div class="tabs is-toggle m-0 fs-13">
|
||||
<ul>
|
||||
<li
|
||||
v-for="tab in tabs"
|
||||
:key="tab.name"
|
||||
:class="activeTab.name === tab.name && 'is-active'"
|
||||
@click="activeTab = tab"
|
||||
>
|
||||
<a>{{ tab.name }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<hr class="m-0" />
|
||||
<div id="tab-content">
|
||||
<div class="is-flex is-gap-1 mb-4">
|
||||
</div>
|
||||
<div class="is-flex is-gap-0.5 is-flex-direction-column mt-4">
|
||||
<p class="icon-text">
|
||||
<span class="icon">
|
||||
<Icon
|
||||
:name="activeTab.icon"
|
||||
:size="21"
|
||||
name="material-symbols:person-outline-rounded"
|
||||
:size="18"
|
||||
/>
|
||||
<span class="fs-15 font-semibold">{{ activeTab.heading }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<OrderProductTab
|
||||
v-if="activeTab.name === 'Chi tiết đơn'"
|
||||
:order="order"
|
||||
</span>
|
||||
<span>{{ invoice.customer_name }}</span>
|
||||
</p>
|
||||
<p class="icon-text">
|
||||
<span class="icon">
|
||||
<Icon
|
||||
name="material-symbols:call-outline-rounded"
|
||||
:size="18"
|
||||
/>
|
||||
<OrderDeliveryTab
|
||||
v-else-if="activeTab.name === 'Giao hàng'"
|
||||
:order="order"
|
||||
</span>
|
||||
<span>{{ invoice.customer_phone }}</span>
|
||||
</p>
|
||||
<p class="icon-text">
|
||||
<span class="icon">
|
||||
<Icon
|
||||
name="material-symbols:calendar-today-outline-rounded"
|
||||
:size="18"
|
||||
/>
|
||||
<OrderReceiptTab
|
||||
v-else-if="activeTab.name === 'Hoá đơn'"
|
||||
:order="order"
|
||||
/>
|
||||
<OrderPaymentTab
|
||||
v-else-if="activeTab.name === 'Thanh toán'"
|
||||
:order="order"
|
||||
/>
|
||||
<OrderHistoryTab
|
||||
v-else-if="activeTab.name === 'Lịch sử'"
|
||||
:order="order"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
<span>{{ $dayjs(invoice.create_time).format("LL") }}</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="px-6 py-4 has-background-primary-95 rounded-lg mt-6">
|
||||
<p>Tổng giá trị đơn hàng</p>
|
||||
<p class="font-bold fs-25">
|
||||
{{ $numtoString(invoice.total_amount, { hasUnit: true }) }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<div class="buttons m-0">
|
||||
<button class="button fs-14 is-flex-grow-1 is-primary">Xác nhận đơn</button>
|
||||
<button class="button fs-14 is-flex-grow-1">Ghi thanh toán</button>
|
||||
</div>
|
||||
<hr />
|
||||
<div class="tabs is-toggle m-0 fs-13">
|
||||
<ul>
|
||||
<li
|
||||
v-for="tab in tabs"
|
||||
:key="tab.name"
|
||||
:class="activeTab.name === tab.name && 'is-active'"
|
||||
@click="activeTab = tab"
|
||||
>
|
||||
<a>{{ tab.name }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div
|
||||
id="tab-content"
|
||||
class="mt-4"
|
||||
>
|
||||
<div class="is-flex is-gap-1 mb-4">
|
||||
<Icon
|
||||
:name="activeTab.icon"
|
||||
:size="21"
|
||||
/>
|
||||
<span class="fs-15 font-semibold">{{ activeTab.heading }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<OrderProductTab
|
||||
v-if="activeTab.name === 'Chi tiết đơn'"
|
||||
:invoice="invoice"
|
||||
/>
|
||||
<OrderDeliveryTab
|
||||
v-else-if="activeTab.name === 'Giao hàng'"
|
||||
:invoice="invoice"
|
||||
/>
|
||||
<OrderReceiptTab
|
||||
v-else-if="activeTab.name === 'Hoá đơn'"
|
||||
:invoice="invoice"
|
||||
/>
|
||||
<OrderPaymentTab
|
||||
v-else-if="activeTab.name === 'Thanh toán'"
|
||||
:invoice="invoice"
|
||||
/>
|
||||
<OrderHistoryTab
|
||||
v-else-if="activeTab.name === 'Lịch sử'"
|
||||
:invoice="invoice"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<style scoped>
|
||||
.card-content {
|
||||
padding: 0; /* 1.5rem */
|
||||
|
||||
> div {
|
||||
padding: var(--bulma-card-content-padding);
|
||||
}
|
||||
}
|
||||
|
||||
.tabs {
|
||||
--bulma-tabs-toggle-link-active-background-color: var(--bulma-link-90);
|
||||
--bulma-tabs-toggle-link-active-background-color: var(--bulma-link-95);
|
||||
--bulma-tabs-toggle-link-active-border-color: var(--bulma-link-90);
|
||||
--bulma-tabs-toggle-link-active-color: var(--bulma-link-40);
|
||||
}
|
||||
|
||||
@@ -17,15 +17,17 @@ function openModal() {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button
|
||||
@click="openModal"
|
||||
class="button is-ghost fs-13"
|
||||
>
|
||||
Chọn IMEI
|
||||
</button>
|
||||
<Modal
|
||||
v-if="showmodal"
|
||||
v-bind="showmodal"
|
||||
@close="showmodal = null"
|
||||
/>
|
||||
<div>
|
||||
<button
|
||||
@click="openModal"
|
||||
class="button is-ghost fs-13"
|
||||
>
|
||||
Chọn IMEI
|
||||
</button>
|
||||
<Modal
|
||||
v-if="showmodal"
|
||||
v-bind="showmodal"
|
||||
@close="showmodal = null"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -70,86 +70,88 @@ onMounted(fetchImeis);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
v-if="isLoading"
|
||||
class="is-flex is-justify-content-center"
|
||||
>
|
||||
<Icon
|
||||
name="svg-spinners:180-ring-with-bg"
|
||||
:size="26"
|
||||
class="has-text-primary"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="imeis.length === 0">
|
||||
<p class="mt-6 mb-8 has-text-centered">Sản phẩm không có IMEI có sẵn nào.</p>
|
||||
<div class="block">
|
||||
<AddIMEIForm
|
||||
:variant="variant"
|
||||
@created="fetchImeis"
|
||||
<div>
|
||||
<div
|
||||
v-if="isLoading"
|
||||
class="is-flex is-justify-content-center"
|
||||
>
|
||||
<Icon
|
||||
name="svg-spinners:180-ring-with-bg"
|
||||
:size="26"
|
||||
class="has-text-primary"
|
||||
/>
|
||||
</div>
|
||||
<div class="block">
|
||||
<ImportData
|
||||
code="imeis"
|
||||
@close="fetchImeis"
|
||||
/>
|
||||
<div v-else-if="imeis.length === 0">
|
||||
<p class="mt-6 mb-8 has-text-centered">Sản phẩm không có IMEI có sẵn nào.</p>
|
||||
<div class="block">
|
||||
<AddIMEIForm
|
||||
:variant="variant"
|
||||
@created="fetchImeis"
|
||||
/>
|
||||
</div>
|
||||
<div class="block">
|
||||
<ImportData
|
||||
code="imeis"
|
||||
@close="fetchImeis"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template v-else>
|
||||
<table class="table is-hoverable is-fullwidth mb-2 fs-13">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>STT</th>
|
||||
<th>IMEI</th>
|
||||
<th>Trạng thái</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="(imeiRec, i) in imeis"
|
||||
:key="imeiRec.id"
|
||||
class="is-clickable"
|
||||
:class="selectedImeis.find((i) => i.imei === imeiRec.imei) && 'is-selected'"
|
||||
@click="toggleSelected(imeiRec)"
|
||||
<template v-else>
|
||||
<table class="table is-hoverable is-fullwidth mb-2 fs-13">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>STT</th>
|
||||
<th>IMEI</th>
|
||||
<th>Trạng thái</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="(imeiRec, i) in imeis"
|
||||
:key="imeiRec.id"
|
||||
class="is-clickable"
|
||||
:class="selectedImeis.find((i) => i.imei === imeiRec.imei) && 'is-selected'"
|
||||
@click="toggleSelected(imeiRec)"
|
||||
>
|
||||
<td class="is-narrow">
|
||||
<label class="checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
:checked="selectedImeis.find((i) => i.imei === imeiRec.imei)"
|
||||
/>
|
||||
</label>
|
||||
</td>
|
||||
<td class="is-narrow">{{ i + 1 }}</td>
|
||||
<td class="is-family-monospace">{{ imeiRec.imei }}</td>
|
||||
<td>
|
||||
<span :class="['tag is-light', imeiRec.deleted ? 'is-danger' : 'is-success']">{{
|
||||
imeiRec.deleted ? "Không có sẵn" : "Có sẵn"
|
||||
}}</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="is-flex is-justify-content-end">
|
||||
<button
|
||||
@click="addToCart"
|
||||
:class="['button is-primary', isAdding && 'is-loading']"
|
||||
:disabled="selectedImeis.length === 0"
|
||||
>
|
||||
<td class="is-narrow">
|
||||
<label class="checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
:checked="selectedImeis.find((i) => i.imei === imeiRec.imei)"
|
||||
/>
|
||||
</label>
|
||||
</td>
|
||||
<td class="is-narrow">{{ i + 1 }}</td>
|
||||
<td class="is-family-monospace">{{ imeiRec.imei }}</td>
|
||||
<td>
|
||||
<span :class="['tag is-light', imeiRec.deleted ? 'is-danger' : 'is-success']">{{
|
||||
imeiRec.deleted ? "Không có sẵn" : "Có sẵn"
|
||||
}}</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="is-flex is-justify-content-end">
|
||||
<button
|
||||
@click="addToCart"
|
||||
:class="['button is-primary', isAdding && 'is-loading']"
|
||||
:disabled="selectedImeis.length === 0"
|
||||
>
|
||||
<span class="icon">
|
||||
<Icon
|
||||
name="material-symbols:add-rounded"
|
||||
:size="18"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
<span>Thêm vào giỏ</span>
|
||||
<span v-if="selectedImeis.length > 0"> ({{ selectedImeis.length }})</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
<span class="icon">
|
||||
<Icon
|
||||
name="material-symbols:add-rounded"
|
||||
:size="18"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
<span>Thêm vào giỏ</span>
|
||||
<span v-if="selectedImeis.length > 0"> ({{ selectedImeis.length }})</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<style scoped>
|
||||
.table tr.is-selected {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import CartItem from "~/components/pos/CartItem.vue";
|
||||
|
||||
const emit = defineEmits(["close"]);
|
||||
const { $patchapi, $deleteapi, $insertapi, $dayjs, $snackbar } = useNuxtApp();
|
||||
const { $id, $patchapi, $deleteapi, $insertapi, $dayjs, $snackbar } = useNuxtApp();
|
||||
const id = "confirmOrder";
|
||||
const isPending = ref(false);
|
||||
const { activeCart, activeCartItems, orderInfo, getCarts } = inject("pos");
|
||||
@@ -19,10 +19,10 @@ async function createOrder() {
|
||||
isPending.value = true;
|
||||
const invoice = await $insertapi("Invoice", {
|
||||
data: {
|
||||
customer: "",
|
||||
customer_name: "",
|
||||
customer_phone: "",
|
||||
customer_email: "",
|
||||
customer: activeCart.value.customer,
|
||||
customer_name: activeCart.value.customer__fullname,
|
||||
customer_phone: activeCart.value.customer__phone,
|
||||
customer_email: activeCart.value.customer__email,
|
||||
shipping_address: shipping_address.value,
|
||||
product_amount: activeCartItems.value.length,
|
||||
shipping_fee: 0,
|
||||
@@ -32,7 +32,7 @@ async function createOrder() {
|
||||
order_type: "pos",
|
||||
status: "pending",
|
||||
ordered_at: new Date(),
|
||||
paid_at: new Date(),
|
||||
paid_at: null,
|
||||
delivery_method: orderInfo.value.deliveryMethod.id,
|
||||
},
|
||||
notify: false,
|
||||
@@ -64,6 +64,27 @@ async function createOrder() {
|
||||
notify: false,
|
||||
});
|
||||
|
||||
if (orderInfo.value.deliveryMethod.code === "HOME_DELIVERY") {
|
||||
const deliveryInfoPayload = {
|
||||
invoice: invoice.id,
|
||||
receiver_name: invoice.customer_name,
|
||||
receiver_phone: invoice.customer_phone,
|
||||
city: orderInfo.value.address.city,
|
||||
district: orderInfo.value.address.district,
|
||||
ward: orderInfo.value.address.ward,
|
||||
address: orderInfo.value.address.address_detail,
|
||||
shipping_fee: 0,
|
||||
carrier: orderInfo.value.deliveryMethod.id,
|
||||
tracking_code: $id(),
|
||||
status: "pending",
|
||||
};
|
||||
|
||||
const deliveryInfo = await $insertapi("Delivery_Info", {
|
||||
data: deliveryInfoPayload,
|
||||
notify: false,
|
||||
});
|
||||
}
|
||||
|
||||
$snackbar("Tạo đơn hàng thành công", "Success");
|
||||
|
||||
await Promise.all([
|
||||
|
||||
@@ -16,6 +16,24 @@ async function updateAddress() {
|
||||
isLoading.value = false;
|
||||
emit("modalevent", { name: "update" });
|
||||
}
|
||||
|
||||
const isDirty = computed(() => {
|
||||
if (props.address) {
|
||||
// edit
|
||||
return !isEqual(props.address, addressRef);
|
||||
} else {
|
||||
return true;
|
||||
// create
|
||||
// true if
|
||||
}
|
||||
});
|
||||
const isValid = computed(() => {
|
||||
if (!addressRef.value.city) return false;
|
||||
if (!addressRef.value.district) return false;
|
||||
if (!addressRef.value.ward) return false;
|
||||
if (!addressRef.value.address_detail) return false;
|
||||
return true;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -41,7 +59,7 @@ async function updateAddress() {
|
||||
v-model="addressRef.ward"
|
||||
class="input"
|
||||
type="text"
|
||||
placeholder="Text input"
|
||||
placeholder="Phường"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -54,7 +72,7 @@ async function updateAddress() {
|
||||
v-model="addressRef.district"
|
||||
class="input"
|
||||
type="text"
|
||||
placeholder="Text input"
|
||||
placeholder="Quận"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -67,7 +85,7 @@ async function updateAddress() {
|
||||
v-model="addressRef.city"
|
||||
class="input"
|
||||
type="text"
|
||||
placeholder="Text input"
|
||||
placeholder="Thành phố"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -94,6 +112,9 @@ async function updateAddress() {
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<pre>address: {{ address }}</pre>
|
||||
<pre>addressRef: {{ addressRef }}</pre>
|
||||
<pre>isDirty: {{ isDirty.toString() }}</pre>
|
||||
<div class="field is-grouped is-grouped-right">
|
||||
<div class="control">
|
||||
<button
|
||||
@@ -108,16 +129,16 @@ async function updateAddress() {
|
||||
<button
|
||||
type="button"
|
||||
:class="['button is-link', isLoading && 'is-loading']"
|
||||
:disabled="isEqual(address, addressRef)"
|
||||
:disabled="!isDirty || !isValid"
|
||||
@click="updateAddress"
|
||||
>
|
||||
<span class="icon">
|
||||
<Icon
|
||||
name="material-symbols:edit-outline-rounded"
|
||||
:size="16"
|
||||
:name="address ? 'material-symbols:edit-outline-rounded' : 'material-symbols:add-rounded'"
|
||||
:size="18"
|
||||
/>
|
||||
</span>
|
||||
<span>Cập nhật</span>
|
||||
<span>{{ address ? "Cập nhật" : "Tạo" }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -49,6 +49,14 @@ function openConfirmModal() {
|
||||
};
|
||||
}
|
||||
|
||||
function openAddAddressModal() {
|
||||
showModal.value = {
|
||||
component: "pos/EditAddress",
|
||||
title: "Thêm địa chỉ",
|
||||
width: "50%",
|
||||
height: "auto",
|
||||
};
|
||||
}
|
||||
provide("pos", {
|
||||
carts,
|
||||
activeCartId,
|
||||
@@ -188,6 +196,7 @@ provide("pos", {
|
||||
column: ['name'],
|
||||
first: true,
|
||||
placeholder: 'Phương thức giao hàng',
|
||||
disabled: !activeCart?.customer,
|
||||
optionid: orderInfo.deliveryMethod?.id,
|
||||
onOption: (e) => (orderInfo.deliveryMethod = e),
|
||||
}"
|
||||
@@ -237,8 +246,23 @@ provide("pos", {
|
||||
@update="getAddresses"
|
||||
/>
|
||||
</div>
|
||||
<div v-else>
|
||||
<p class="has-text-grey has-text-centered p-4">Khách hàng chưa có địa chỉ</p>
|
||||
<div
|
||||
v-else
|
||||
class="has-text-centered"
|
||||
>
|
||||
<p class="has-text-grey p-4">Khách hàng chưa có địa chỉ</p>
|
||||
<button
|
||||
@click="openAddAddressModal"
|
||||
class="button is-light is-primary"
|
||||
>
|
||||
<span class="icon">
|
||||
<Icon
|
||||
name="material-symbols:add-home-work-outline-rounded"
|
||||
:size="18"
|
||||
/>
|
||||
</span>
|
||||
<span>Thêm địa chỉ</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -270,6 +294,7 @@ provide("pos", {
|
||||
column: ['name'],
|
||||
first: true,
|
||||
placeholder: 'Phương thức thanh toán',
|
||||
disabled: !activeCart?.customer,
|
||||
optionid: orderInfo.paymentMethod?.id,
|
||||
onOption: (e) => (orderInfo.paymentMethod = e),
|
||||
}"
|
||||
|
||||
@@ -485,13 +485,20 @@ export default /** @type {const} */ ([
|
||||
name: "Invoice",
|
||||
url: "data/Invoice/",
|
||||
url_detail: "data-detail/Invoice/",
|
||||
params: {},
|
||||
params: {
|
||||
values:
|
||||
"delivery,delivery__receiver_name,delivery__receiver_phone,delivery__tracking_code,delivery__expected_date,delivery__delivered_date,delivery__status,payments,history,id,code,customer,staff,staff__fullname,staff__phone,staff__email,voucher,customer_name,customer_phone,customer_email,shipping_address,product_amount,shipping_fee,total_amount,discount_amount,final_amount,order_type,status,note,ordered_at,paid_at,creator,updater,deleted,delivery_method,create_time,update_time",
|
||||
sort: "-id",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Invoice_Detail",
|
||||
url: "data/Invoice_Detail/",
|
||||
url_detail: "data-detail/Invoice_Detail/",
|
||||
params: {},
|
||||
params: {
|
||||
values:
|
||||
"id,code,invoice,variant,variant__code,variant__product,variant__product__name,variant__color,variant__price,variant__image__code,imei_sold,price,status,note,deleted,create_time,update_time",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Manufacturer",
|
||||
@@ -647,6 +654,12 @@ export default /** @type {const} */ ([
|
||||
url_detail: "data-detail/Customer_Address/",
|
||||
params: {},
|
||||
},
|
||||
{
|
||||
name: "Delivery_Info",
|
||||
url: "data/Delivery_Info/",
|
||||
url_detail: "data-detail/Delivery_Info/",
|
||||
params: {},
|
||||
},
|
||||
{
|
||||
name: "Delivery_Method",
|
||||
url: "data/Delivery_Method/",
|
||||
|
||||
Reference in New Issue
Block a user