changes
This commit is contained in:
@@ -1,5 +1,4 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
const { $shortenCurrency } = useNuxtApp();
|
|
||||||
const revenueChartOptions = {
|
const revenueChartOptions = {
|
||||||
chart: {
|
chart: {
|
||||||
type: "spline",
|
type: "spline",
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ const props = defineProps({
|
|||||||
order_count: Number,
|
order_count: Number,
|
||||||
paid: Number,
|
paid: Number,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { $shortenCurrency } = useNuxtApp();
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="is-flex is-gap-1 is-justify-content-space-between">
|
<div class="is-flex is-gap-1 is-justify-content-space-between">
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ const props = defineProps({
|
|||||||
revenue: Number,
|
revenue: Number,
|
||||||
topRevenue: Number,
|
topRevenue: Number,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { $shortenCurrency } = useNuxtApp();
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ const props = defineProps({
|
|||||||
invoice: Object,
|
invoice: Object,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { $getdata, $dayjs, $shortenCurrency } = useNuxtApp();
|
const { $getdata } = useNuxtApp();
|
||||||
const paymentRecords = ref([]);
|
const paymentRecords = ref([]);
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
paymentRecords.value = await $getdata("Payment_Record", {
|
paymentRecords.value = await $getdata("Payment_Record", {
|
||||||
@@ -59,9 +59,12 @@ function openModal() {
|
|||||||
</td>
|
</td>
|
||||||
<td class="has-text-centered">
|
<td class="has-text-centered">
|
||||||
<span
|
<span
|
||||||
:class="['tag rounded-full', `has-background-${invoice.status__color}-80 has-text-${invoice.status__color}-25`]"
|
:class="[
|
||||||
|
'tag rounded-full',
|
||||||
|
`has-background-${invoice.invoice_status__color}-90 has-text-${invoice.invoice_status__color}-25`,
|
||||||
|
]"
|
||||||
>
|
>
|
||||||
{{ invoice.status }}
|
{{ invoice.invoice_status__name }}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
@@ -70,8 +73,8 @@ function openModal() {
|
|||||||
</p>
|
</p>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<p :class="`has-text-${invoice.delivery_status__color}-40`">
|
<p :class="`has-text-${invoice.delivery__delivery_status__color}-40`">
|
||||||
{{ invoice.delivery__status }}
|
{{ invoice.delivery__delivery_status__name }}
|
||||||
</p>
|
</p>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ const props = defineProps({
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<p class="font-medium has-text-primary-50">
|
<p class="font-medium has-text-primary-50">
|
||||||
{{ capitalize(invoice.delivery__status) }}
|
{{ capitalize(invoice.delivery__delivery_status__name) }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -18,12 +18,9 @@ const props = defineProps({
|
|||||||
<span class="fs-13 has-text-grey">{{ unit }}</span>
|
<span class="fs-13 has-text-grey">{{ unit }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div :class="['p-3 is-flex rounded-lg', `has-background-${color}-soft`]">
|
||||||
:class="['p-3 is-flex', `has-background-${color}-soft`]"
|
|
||||||
style="border-radius: 8px"
|
|
||||||
>
|
|
||||||
<Icon
|
<Icon
|
||||||
:name="icon"
|
:name="icon || 'svg-spinners:180-ring-with-bg'"
|
||||||
:size="26"
|
:size="26"
|
||||||
:class="`has-text-${color}-40`"
|
:class="`has-text-${color}-40`"
|
||||||
/>
|
/>
|
||||||
|
|||||||
43
app/components/orders/OrderHighlights.vue
Normal file
43
app/components/orders/OrderHighlights.vue
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<script setup>
|
||||||
|
import { useInvoices } from "~/components/orders/composables/useInvoices";
|
||||||
|
import OrderHighlightCard from "~/components/orders/OrderHighlightCard.vue";
|
||||||
|
|
||||||
|
const store = useStore();
|
||||||
|
const { invoices, invoicesByStatus } = useInvoices();
|
||||||
|
|
||||||
|
const iconMap = {
|
||||||
|
DRAFT: "material-symbols:assignment-outline-rounded",
|
||||||
|
CONFIRMED: "material-symbols:check-circle-outline-rounded",
|
||||||
|
SHIPPING: "material-symbols:delivery-truck-speed-outline-rounded",
|
||||||
|
COMPLETED: "material-symbols:box-outline-rounded",
|
||||||
|
};
|
||||||
|
|
||||||
|
const revenue = computed(() => {
|
||||||
|
return invoices.value.reduce((prev, curr) => prev + curr.final_amount, 0);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="fixed-grid has-2-cols-mobile has-5-cols">
|
||||||
|
<div class="grid">
|
||||||
|
<OrderHighlightCard
|
||||||
|
v-for="invoiceStatus in store.Invoice_Status"
|
||||||
|
:key="invoiceStatus.id"
|
||||||
|
v-bind="{
|
||||||
|
...invoiceStatus,
|
||||||
|
value: invoicesByStatus[invoiceStatus.code]?.length || 0,
|
||||||
|
icon: iconMap[invoiceStatus.code],
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
<OrderHighlightCard
|
||||||
|
v-bind="{
|
||||||
|
name: 'Doanh thu',
|
||||||
|
value: $shortenCurrency(revenue),
|
||||||
|
icon: 'material-symbols:attach-money-rounded',
|
||||||
|
color: 'purple',
|
||||||
|
unit: 'VNĐ',
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -1,20 +1,17 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
order: Object,
|
invoice: Object,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { $dayjs, $shortenCurrency } = useNuxtApp();
|
|
||||||
const emit = defineEmits(["selectOrder", "unselect"]);
|
|
||||||
const showModal = ref();
|
const showModal = ref();
|
||||||
|
|
||||||
function openModal() {
|
function openModal() {
|
||||||
showModal.value = {
|
showModal.value = {
|
||||||
component: "orders/SelectedOrder",
|
component: "orders/SelectedOrder",
|
||||||
title: "SelectedOrder",
|
title: "Chi tiết đơn hàng",
|
||||||
width: "75%",
|
width: "min(700px, 75%)",
|
||||||
height: "500px",
|
|
||||||
vbind: {
|
vbind: {
|
||||||
order: props.order,
|
invoice: props.invoice,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -22,32 +19,32 @@ function openModal() {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
:class="['card fs-14 is-clickable', `has-background-${order.status__color}-95`]"
|
:class="['card fs-14 is-clickable', `has-background-${invoice.invoice_status__color}-100`]"
|
||||||
:style="{ border: `1px solid var(--bulma-${order.status__color}-60)` }"
|
:style="{ border: `1px solid var(--bulma-${invoice.invoice_status__color}-80)` }"
|
||||||
@click="selected ? emit('unselect') : emit('selectOrder', order.id)"
|
@click="openModal"
|
||||||
>
|
>
|
||||||
<div class="card-content p-4">
|
<div class="card-content p-4">
|
||||||
<div class="mb-4 is-flex is-justify-content-space-between is-gap-1">
|
<div class="mb-4 is-flex is-justify-content-space-between is-gap-1">
|
||||||
<p class="fs-15 font-bold">{{ order.code }}</p>
|
<p class="fs-15 font-bold">{{ invoice.code }}</p>
|
||||||
<span :class="['fs-13', `has-text-${order.payment_status__color}-40`]">
|
<span :class="['fs-13', `has-text-${invoice.payment_status__color}-40`]">
|
||||||
{{ order.payment_status__name }}
|
{{ invoice.payment_status__name }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="is-flex is-flex-direction-column is-gap-2">
|
<div class="is-flex is-flex-direction-column is-gap-2">
|
||||||
<!-- customer info -->
|
<!-- customer info -->
|
||||||
<div>
|
<div>
|
||||||
<p class="has-text-grey-20">{{ order.customer__name }}</p>
|
<p class="has-text-grey-20">{{ invoice.customer_name }}</p>
|
||||||
<div class="has-text-grey fs-13 mt-1 is-flex is-gap-1 is-align-items-center">
|
<div class="has-text-grey fs-13 mt-1 is-flex is-gap-1 is-align-items-center">
|
||||||
<Icon name="material-symbols:call-outline-rounded" />
|
<Icon name="material-symbols:call-outline-rounded" />
|
||||||
<p>{{ order.customer__phone }}</p>
|
<p>{{ invoice.customer_phone }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- product info -->
|
<!-- product info -->
|
||||||
<div>
|
<div>
|
||||||
<p class="fs-24 has-text-grey-20 font-bold">
|
<p class="fs-24 has-text-grey-20 font-bold">
|
||||||
{{ $shortenCurrency(order.total) }}
|
{{ $shortenCurrency(invoice.final_amount) }}
|
||||||
</p>
|
</p>
|
||||||
<p class="fs-13 has-text-grey">{{ order.order__products.length }} sản phẩm</p>
|
<p class="fs-13 has-text-grey">{{ invoice.product_amount }} sản phẩm</p>
|
||||||
</div>
|
</div>
|
||||||
<hr class="m-0 has-background-grey-85" />
|
<hr class="m-0 has-background-grey-85" />
|
||||||
<div class="is-flex is-flex-direction-column is-gap-0.5 fs-13 has-text-grey">
|
<div class="is-flex is-flex-direction-column is-gap-0.5 fs-13 has-text-grey">
|
||||||
@@ -56,29 +53,29 @@ function openModal() {
|
|||||||
name="material-symbols:calendar-today-outline-rounded"
|
name="material-symbols:calendar-today-outline-rounded"
|
||||||
:size="16"
|
:size="16"
|
||||||
/>
|
/>
|
||||||
<span>{{ $dayjs(order.create_time).format("L") }}</span>
|
<span>{{ $dayjs(invoice.create_time).format("L") }}</span>
|
||||||
<span>•</span>
|
<span>•</span>
|
||||||
<span>{{ $dayjs(order.create_time).format("HH:mm") }}</span>
|
<span>{{ $dayjs(invoice.create_time).format("HH:mm") }}</span>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
NV: <span>{{ order.employee__name }}</span>
|
NV: <span>{{ invoice.staff__fullname }}</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
v-if="order.status__name !== 'Hoàn thành'"
|
v-if="invoice.invoice_status__code !== 'COMPLETED'"
|
||||||
:class="[
|
:class="[
|
||||||
'button fs-14 has-text-white',
|
'button fs-14 has-text-white',
|
||||||
order.status__name === 'Nháp'
|
invoice.invoice_status__code === 'DRAFT'
|
||||||
? 'is-primary'
|
? 'is-primary'
|
||||||
: order.status__name === 'Đã xác nhận'
|
: invoice.invoice_status__code === 'CONFIRMED'
|
||||||
? 'is-orange'
|
? 'is-orange'
|
||||||
: 'is-success',
|
: 'is-success',
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
{{
|
{{
|
||||||
order.status__name === "Nháp"
|
invoice.invoice_status__code === "DRAFT"
|
||||||
? "Xác nhận"
|
? "Xác nhận"
|
||||||
: order.status__name === "Đã xác nhận"
|
: invoice.invoice_status__code === "CONFIRMED"
|
||||||
? "Giao hàng"
|
? "Giao hàng"
|
||||||
: "Hoàn thành"
|
: "Hoàn thành"
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -1,36 +1,9 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
|
import { useInvoices } from "~/components/orders/composables/useInvoices";
|
||||||
import PipelinePhase from "~/components/orders/PipelinePhase.vue";
|
import PipelinePhase from "~/components/orders/PipelinePhase.vue";
|
||||||
|
|
||||||
const phases = [
|
const store = useStore();
|
||||||
{
|
const { invoices } = useInvoices();
|
||||||
name: "Nháp",
|
|
||||||
value: 2,
|
|
||||||
icon: "material-symbols:assignment-outline-rounded",
|
|
||||||
color: "yellow",
|
|
||||||
index: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Đã xác nhận",
|
|
||||||
value: 3,
|
|
||||||
icon: "material-symbols:check-circle-outline-rounded",
|
|
||||||
color: "blue",
|
|
||||||
index: 2,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Đang giao",
|
|
||||||
value: 2,
|
|
||||||
icon: "material-symbols:delivery-truck-speed-outline-rounded",
|
|
||||||
color: "orange",
|
|
||||||
index: 3,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Hoàn thành",
|
|
||||||
value: 1,
|
|
||||||
icon: "material-symbols:box-outline-rounded",
|
|
||||||
color: "green",
|
|
||||||
index: 4,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
@@ -38,16 +11,16 @@ const phases = [
|
|||||||
<p class="fs-16 font-semibold mb-4">Pipeline đơn hàng</p>
|
<p class="fs-16 font-semibold mb-4">Pipeline đơn hàng</p>
|
||||||
<div class="is-flex is-justify-content-space-evenly">
|
<div class="is-flex is-justify-content-space-evenly">
|
||||||
<PipelinePhase
|
<PipelinePhase
|
||||||
v-for="phase in phases"
|
v-for="invoiceStatus in store.Invoice_Status"
|
||||||
:key="phases.name"
|
:key="invoiceStatus.id"
|
||||||
v-bind="phase"
|
v-bind="invoiceStatus"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<hr class="has-background-grey-85" />
|
<hr class="has-background-grey-85" />
|
||||||
<div class="is-flex is-justify-content-space-between">
|
<div class="is-flex is-justify-content-space-between">
|
||||||
<p>Tổng đơn hàng</p>
|
<p>Tổng đơn hàng</p>
|
||||||
<p class="fs-18 font-semibold">
|
<p class="fs-18 font-semibold">
|
||||||
{{ phases.reduce((prev, curr) => prev + curr.value, 0) }}
|
{{ invoices.length }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,42 +1,10 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import OrderHighlightCard from "~/components/orders/OrderHighlightCard.vue";
|
import { useInvoices } from "~/components/orders/composables/useInvoices";
|
||||||
|
import OrderHighlights from "~/components/orders/OrderHighlights.vue";
|
||||||
import OrderPipeline from "~/components/orders/OrderPipeline.vue";
|
import OrderPipeline from "~/components/orders/OrderPipeline.vue";
|
||||||
import OrdersTable from "~/components/orders/OrdersTable.vue";
|
import OrdersTable from "~/components/orders/OrdersTable.vue";
|
||||||
|
|
||||||
const { $getdata, $store } = useNuxtApp();
|
const store = useStore();
|
||||||
const highlights = [
|
|
||||||
{
|
|
||||||
name: "Nháp",
|
|
||||||
value: 2,
|
|
||||||
icon: "material-symbols:assignment-outline-rounded",
|
|
||||||
color: "yellow",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Đã xác nhận",
|
|
||||||
value: 3,
|
|
||||||
icon: "material-symbols:check-circle-outline-rounded",
|
|
||||||
color: "blue",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Đang giao",
|
|
||||||
value: 2,
|
|
||||||
icon: "material-symbols:delivery-truck-speed-outline-rounded",
|
|
||||||
color: "orange",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Hoàn thành",
|
|
||||||
value: 1,
|
|
||||||
icon: "material-symbols:box-outline-rounded",
|
|
||||||
color: "green",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Doanh thu",
|
|
||||||
value: "6.8M",
|
|
||||||
icon: "material-symbols:attach-money-rounded",
|
|
||||||
color: "purple",
|
|
||||||
unit: "VNĐ",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const viewModes = [
|
const viewModes = [
|
||||||
{ name: "list", icon: "material-symbols:format-list-bulleted-rounded" },
|
{ name: "list", icon: "material-symbols:format-list-bulleted-rounded" },
|
||||||
@@ -44,20 +12,15 @@ const viewModes = [
|
|||||||
];
|
];
|
||||||
const viewMode = ref("list");
|
const viewMode = ref("list");
|
||||||
|
|
||||||
const invoices = ref();
|
const { fetchInvoices } = useInvoices();
|
||||||
onMounted(async () => {
|
onMounted(fetchInvoices);
|
||||||
invoices.value = await $getdata("Invoice");
|
|
||||||
});
|
|
||||||
provide("orders", {
|
|
||||||
invoices,
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<Teleport
|
<Teleport
|
||||||
defer
|
defer
|
||||||
to="#header-right-slot"
|
to="#header-right-slot"
|
||||||
v-if="$store.tabinfo.tab.code === 'orders'"
|
v-if="store.tabinfo.tab.code === 'orders'"
|
||||||
>
|
>
|
||||||
<div class="is-flex is-gap-2 is-justify-content-flex-end is-align-items-center">
|
<div class="is-flex is-gap-2 is-justify-content-flex-end is-align-items-center">
|
||||||
<div class="tabs is-toggle m-0">
|
<div class="tabs is-toggle m-0">
|
||||||
@@ -90,15 +53,7 @@ provide("orders", {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</Teleport>
|
</Teleport>
|
||||||
<div class="fixed-grid has-2-cols-mobile has-5-cols">
|
<OrderHighlights />
|
||||||
<div class="grid">
|
|
||||||
<OrderHighlightCard
|
|
||||||
v-for="highlight in highlights"
|
|
||||||
:key="highlight.name"
|
|
||||||
v-bind="highlight"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<OrderPipeline />
|
<OrderPipeline />
|
||||||
<OrdersTable :viewMode="viewMode" />
|
<OrdersTable :viewMode="viewMode" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,14 +6,14 @@ const props = defineProps({
|
|||||||
statuses: Array,
|
statuses: Array,
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(["selectOrder", "unselect"]);
|
const store = useStore();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="p-5 fixed-grid has-4-cols">
|
<div class="p-5 fixed-grid has-4-cols">
|
||||||
<div class="grid">
|
<div class="grid">
|
||||||
<div
|
<div
|
||||||
v-for="status in statuses"
|
v-for="status in store.Invoice_Status"
|
||||||
:key="status.id"
|
:key="status.id"
|
||||||
class="card"
|
class="card"
|
||||||
style="border: none"
|
style="border: none"
|
||||||
@@ -30,18 +30,16 @@ const emit = defineEmits(["selectOrder", "unselect"]);
|
|||||||
class="px-2 py-1 font-semibold rounded-lg has-background-white"
|
class="px-2 py-1 font-semibold rounded-lg has-background-white"
|
||||||
:style="{ border: `1px solid var(--bulma-${status.color}-60)` }"
|
:style="{ border: `1px solid var(--bulma-${status.color}-60)` }"
|
||||||
>
|
>
|
||||||
{{ orders.filter((o) => o.status === status.id).length }}
|
{{ orders.filter((o) => o.invoice_status === status.id).length }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<hr class="m-0 has-background-grey-85" />
|
<hr class="m-0 has-background-grey-85" />
|
||||||
<div class="has-background-grey-100 p-4">
|
<div class="has-background-grey-100 p-4">
|
||||||
<OrderKanbanCard
|
<OrderKanbanCard
|
||||||
v-if="orders.filter((o) => o.status === status.id).length > 0"
|
v-if="orders.filter((o) => o.invoice_status === status.id).length > 0"
|
||||||
v-for="order in orders.filter((o) => o.status === status.id)"
|
v-for="order in orders.filter((o) => o.invoice_status === status.id)"
|
||||||
:key="order.id"
|
:key="order.id"
|
||||||
:order="order"
|
:invoice="order"
|
||||||
@selectOrder="emit('selectOrder', order.id)"
|
|
||||||
@unselect="emit('unselect')"
|
|
||||||
/>
|
/>
|
||||||
<p
|
<p
|
||||||
v-else
|
v-else
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { pull } from "es-toolkit";
|
import { pull } from "es-toolkit";
|
||||||
|
import { useInvoices } from "~/components/orders/composables/useInvoices";
|
||||||
import InvoiceRow from "~/components/orders/InvoiceRow.vue";
|
import InvoiceRow from "~/components/orders/InvoiceRow.vue";
|
||||||
import OrdersKanban from "~/components/orders/OrdersKanban.vue";
|
import OrdersKanban from "~/components/orders/OrdersKanban.vue";
|
||||||
|
|
||||||
@@ -7,8 +8,9 @@ const props = defineProps({
|
|||||||
viewMode: String,
|
viewMode: String,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { $findapi, $getapi, $dayjs } = useNuxtApp();
|
const { $getdata, $dayjs } = useNuxtApp();
|
||||||
const { invoices } = inject("orders");
|
const { invoices, isPending } = useInvoices();
|
||||||
|
const store = useStore();
|
||||||
|
|
||||||
const statuses = [
|
const statuses = [
|
||||||
{
|
{
|
||||||
@@ -33,15 +35,10 @@ const statuses = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const paymentStatuses = ref([]);
|
|
||||||
const employees = ref([]);
|
const employees = ref([]);
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
const apis = $findapi(["Payment_Status", "Staff"]);
|
employees.value = await $getdata("Staff");
|
||||||
const [paymentStatusRes, staffRes] = await $getapi(apis);
|
|
||||||
|
|
||||||
paymentStatuses.value = paymentStatusRes.data.rows || [];
|
|
||||||
employees.value = staffRes.data.rows || [];
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const input = ref();
|
const input = ref();
|
||||||
@@ -65,7 +62,7 @@ const filteredInvoices = computed(() => {
|
|||||||
|
|
||||||
const filteredByStatuses = filteredByInput.filter((order) => {
|
const filteredByStatuses = filteredByInput.filter((order) => {
|
||||||
if (selectedStatuses.value.length === 0) return true;
|
if (selectedStatuses.value.length === 0) return true;
|
||||||
return selectedStatuses.value.includes(order.status);
|
return selectedStatuses.value.includes(order.invoice_status);
|
||||||
});
|
});
|
||||||
|
|
||||||
const filteredByPaymentStatuses = filteredByStatuses.filter((order) => {
|
const filteredByPaymentStatuses = filteredByStatuses.filter((order) => {
|
||||||
@@ -148,7 +145,7 @@ function toggleStatus(id) {
|
|||||||
<select v-model="selectedPaymentStatus">
|
<select v-model="selectedPaymentStatus">
|
||||||
<option :value="undefined">Thanh toán</option>
|
<option :value="undefined">Thanh toán</option>
|
||||||
<option
|
<option
|
||||||
v-for="paymentStatus in paymentStatuses"
|
v-for="paymentStatus in store.Payment_Status"
|
||||||
:key="paymentStatus.id"
|
:key="paymentStatus.id"
|
||||||
:value="paymentStatus.id"
|
:value="paymentStatus.id"
|
||||||
>
|
>
|
||||||
@@ -171,7 +168,7 @@ function toggleStatus(id) {
|
|||||||
</div>
|
</div>
|
||||||
<div class="is-flex is-gap-1 mt-3">
|
<div class="is-flex is-gap-1 mt-3">
|
||||||
<button
|
<button
|
||||||
v-for="status in statuses"
|
v-for="status in store.Invoice_Status"
|
||||||
:key="status.id"
|
:key="status.id"
|
||||||
:class="[
|
:class="[
|
||||||
'tag fs-13 is-rounded',
|
'tag fs-13 is-rounded',
|
||||||
@@ -218,6 +215,15 @@ function toggleStatus(id) {
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
<tr v-if="isPending">
|
||||||
|
<td colspan="8">
|
||||||
|
<Icon
|
||||||
|
name="svg-spinners:180-ring-with-bg"
|
||||||
|
:size="20"
|
||||||
|
class="has-text-primary is-block mx-auto"
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<InvoiceRow
|
<InvoiceRow
|
||||||
v-for="invoice in filteredInvoices"
|
v-for="invoice in filteredInvoices"
|
||||||
:key="invoice.id"
|
:key="invoice.id"
|
||||||
|
|||||||
@@ -1,28 +1,39 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
|
import { useInvoices } from "~/components/orders/composables/useInvoices";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
id: Number,
|
||||||
|
code: String,
|
||||||
name: String,
|
name: String,
|
||||||
value: Number,
|
en: String,
|
||||||
icon: String,
|
|
||||||
color: String,
|
color: String,
|
||||||
index: Number,
|
index: Number,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { invoices, invoicesByStatus } = useInvoices();
|
||||||
|
|
||||||
|
const invoiceMatchCount = computed(() => {
|
||||||
|
if (!props.code) return 0;
|
||||||
|
if (!invoicesByStatus.value[props.code]) return 0;
|
||||||
|
return invoicesByStatus.value[props.code].length;
|
||||||
|
});
|
||||||
|
const progressVal = computed(() => (invoiceMatchCount.value * 100) / (invoices.value.length || 1));
|
||||||
const progressUnfilled = computed(() => `var(--bulma-${props.color}-85)`);
|
const progressUnfilled = computed(() => `var(--bulma-${props.color}-85)`);
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="is-flex-grow-1">
|
<div class="is-flex-grow-1">
|
||||||
<div class="is-flex is-justify-content-space-between">
|
<div class="is-flex is-justify-content-space-between">
|
||||||
<p :class="`has-text-${color}-40`">{{ name }}</p>
|
<p :class="`has-text-${color}-40`">{{ name }}</p>
|
||||||
<p :class="['fs-18 font-bold', `has-text-${color}-30`]">{{ value }}</p>
|
<p :class="['fs-18 font-bold', `has-text-${color}-30`]">{{ invoiceMatchCount }}</p>
|
||||||
</div>
|
</div>
|
||||||
<progress
|
<progress
|
||||||
:class="['progress is-small mt-2', `is-${color}`]"
|
:class="['progress is-small mt-2', `is-${color}`]"
|
||||||
value="20"
|
:value="progressVal"
|
||||||
max="100"
|
max="100"
|
||||||
></progress>
|
></progress>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="index < 4"
|
v-if="index < 3"
|
||||||
class="is-flex is-justify-content-center is-align-items-center"
|
class="is-flex is-justify-content-center is-align-items-center"
|
||||||
style="width: 60px; height: 48px"
|
style="width: 60px; height: 48px"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -51,10 +51,10 @@ const activeTab = ref(tabs[0]);
|
|||||||
<span
|
<span
|
||||||
:class="[
|
:class="[
|
||||||
'tag rounded-full',
|
'tag rounded-full',
|
||||||
`has-background-${invoice.status__color}-80 has-text-${invoice.status__color}-25`,
|
`has-background-${invoice.invoice_status__color}-80 has-text-${invoice.invoice_status__color}-25`,
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
{{ invoice.status }}
|
{{ invoice.invoice_status__name }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="is-flex is-gap-0.5 is-flex-direction-column mt-4">
|
<div class="is-flex is-gap-0.5 is-flex-direction-column mt-4">
|
||||||
|
|||||||
20
app/components/orders/composables/useInvoices.js
Normal file
20
app/components/orders/composables/useInvoices.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { groupBy } from "es-toolkit";
|
||||||
|
|
||||||
|
const invoices = ref([]);
|
||||||
|
const isPending = ref(false);
|
||||||
|
|
||||||
|
export function useInvoices() {
|
||||||
|
const { $getdata } = useNuxtApp();
|
||||||
|
|
||||||
|
async function fetchInvoices() {
|
||||||
|
isPending.value = true;
|
||||||
|
invoices.value = await $getdata("Invoice");
|
||||||
|
isPending.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const invoicesByStatus = computed(() => {
|
||||||
|
return groupBy(invoices.value, (invoice) => invoice.invoice_status__code);
|
||||||
|
});
|
||||||
|
|
||||||
|
return { invoices, isPending, fetchInvoices, invoicesByStatus };
|
||||||
|
}
|
||||||
@@ -487,7 +487,7 @@ export default /** @type {const} */ ([
|
|||||||
url_detail: "data-detail/Invoice/",
|
url_detail: "data-detail/Invoice/",
|
||||||
params: {
|
params: {
|
||||||
values:
|
values:
|
||||||
"delivery,delivery__receiver_name,delivery__receiver_phone,delivery__tracking_code,delivery__expected_date,delivery__delivered_date,delivery__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,invoice_status,invoice_status__code,invoice_status__name,invoice_status__color,payment_status,payment_status__code,payment_status__name,payment_status__color,note,ordered_at,paid_at,creator,updater,deleted,delivery_method,create_time,update_time",
|
"delivery,delivery__receiver_name,delivery__receiver_phone,delivery__tracking_code,delivery__expected_date,delivery__delivered_date,delivery__delivery_status,delivery__delivery_status__code,delivery__delivery_status__name,delivery__delivery_status__color,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,invoice_status,invoice_status__code,invoice_status__name,invoice_status__color,payment_status,payment_status__code,payment_status__name,payment_status__color,note,ordered_at,paid_at,creator,updater,deleted,delivery_method,create_time,update_time",
|
||||||
sort: "-id",
|
sort: "-id",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -505,7 +505,7 @@ export default /** @type {const} */ ([
|
|||||||
commit: "Invoice_Status",
|
commit: "Invoice_Status",
|
||||||
url: "data/Invoice_Status/",
|
url: "data/Invoice_Status/",
|
||||||
url_detail: "data-detail/Invoice_Status/",
|
url_detail: "data-detail/Invoice_Status/",
|
||||||
params: {},
|
params: { sort: "index" },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Manufacturer",
|
name: "Manufacturer",
|
||||||
@@ -666,7 +666,7 @@ export default /** @type {const} */ ([
|
|||||||
commit: "Payment_Status",
|
commit: "Payment_Status",
|
||||||
url: "data/Payment_Status/",
|
url: "data/Payment_Status/",
|
||||||
url_detail: "data-detail/Payment_Status/",
|
url_detail: "data-detail/Payment_Status/",
|
||||||
params: {},
|
params: { sort: "index" },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Customer_Address",
|
name: "Customer_Address",
|
||||||
@@ -691,7 +691,7 @@ export default /** @type {const} */ ([
|
|||||||
commit: "Delivery_Status",
|
commit: "Delivery_Status",
|
||||||
url: "data/Delivery_Status/",
|
url: "data/Delivery_Status/",
|
||||||
url_detail: "data-detail/Delivery_Status/",
|
url_detail: "data-detail/Delivery_Status/",
|
||||||
params: {},
|
params: { sort: "index" },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Cart",
|
name: "Cart",
|
||||||
|
|||||||
Reference in New Issue
Block a user