feat: build UI
This commit is contained in:
34
app/components/orders/OrderHighlightCard.vue
Normal file
34
app/components/orders/OrderHighlightCard.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
name: String,
|
||||
value: Number,
|
||||
icon: String,
|
||||
color: String,
|
||||
unit: String
|
||||
})
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div class="cell">
|
||||
<div class="card h-full">
|
||||
<div class="card-content is-flex is-gap-1 is-justify-content-space-between is-align-items-center">
|
||||
<div>
|
||||
<p class="has-text-grey">{{ name }}</p>
|
||||
<div class="is-flex is-gap-1 is-align-items-center">
|
||||
<p class="fs-26 font-bold">{{ value }}</p>
|
||||
<span class="fs-13 has-text-grey">{{ unit }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
:class="['p-3 is-flex', `has-background-${color}-soft`]" style="border-radius: 8px"
|
||||
>
|
||||
<Icon
|
||||
:name="icon"
|
||||
:size="26"
|
||||
:class="`has-text-${color}-40`"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
53
app/components/orders/OrderPipeline.vue
Normal file
53
app/components/orders/OrderPipeline.vue
Normal file
@@ -0,0 +1,53 @@
|
||||
<script setup>
|
||||
import PipelinePhase from '@/components/orders/PipelinePhase.vue';
|
||||
|
||||
const phases = [
|
||||
{
|
||||
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>
|
||||
<template>
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<p class="fs-17 font-semibold mb-4">Pipeline đơn hàng</p>
|
||||
<div class="is-flex is-justify-content-space-evenly">
|
||||
<PipelinePhase
|
||||
v-for="phase in phases"
|
||||
:key="phases.name"
|
||||
v-bind="phase"
|
||||
/>
|
||||
</div>
|
||||
<hr class="has-background-grey-lighter" />
|
||||
<div class="is-flex is-justify-content-space-between">
|
||||
<p>Tổng đơn hàng</p>
|
||||
<p class="fs-18 font-semibold">{{ phases.reduce((prev, curr) => prev + curr.value, 0) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
85
app/components/orders/OrderRow.vue
Normal file
85
app/components/orders/OrderRow.vue
Normal file
@@ -0,0 +1,85 @@
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
id: Number,
|
||||
code: String,
|
||||
employee: Number,
|
||||
employee__name: String,
|
||||
customer: Number,
|
||||
customer__name: String,
|
||||
customer__phone: String,
|
||||
total: String,
|
||||
status: Number,
|
||||
status__name: String,
|
||||
status__color: String,
|
||||
payment_status: Number,
|
||||
payment_status__name: String,
|
||||
payment_status__color: String,
|
||||
delivery_status: Number,
|
||||
delivery_status__name: String,
|
||||
delivery_status__color: String,
|
||||
order__products: Array,
|
||||
create_time: String
|
||||
});
|
||||
|
||||
const { $dayjs, $shortenCurrency } = useNuxtApp();
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<tr>
|
||||
<td>
|
||||
<div>
|
||||
<p class="fs-15 font-semibold">{{ code }}</p>
|
||||
<p class="fs-13 has-text-grey">{{ employee__name }}</p>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div>
|
||||
<p>{{ customer__name }}</p>
|
||||
<div class="is-flex is-gap-0.5 is-align-items-center mt-1 fs-13 has-text-grey">
|
||||
<Icon name="material-symbols:call-outline-rounded"
|
||||
:size="15"
|
||||
/>
|
||||
<span class="fs-12">{{ customer__phone }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="has-text-right">
|
||||
<div>
|
||||
<p class="font-semibold">{{ $shortenCurrency(total) }}</p>
|
||||
<p class="fs-13 has-text-grey">{{ order__products.length }} SP</p>
|
||||
</div>
|
||||
</td>
|
||||
<td class="has-text-centered">
|
||||
<span :class="[
|
||||
'tag rounded-full',
|
||||
`has-background-${status__color}-80 has-text-${status__color}-25`
|
||||
]">
|
||||
{{ status__name }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<p :class="`has-text-${payment_status__color}-40`">{{ payment_status__name }}</p>
|
||||
</td>
|
||||
<td>
|
||||
<p :class="`has-text-${delivery_status__color}-40`">{{ delivery_status__name }}</p>
|
||||
</td>
|
||||
<td>
|
||||
<div>
|
||||
<div class="is-flex is-gap-0.5">
|
||||
<Icon :size="18" name="material-symbols:calendar-month-outline-rounded" />
|
||||
<span class="fs-13">{{ $dayjs(create_time).format('L') }}</span>
|
||||
</div>
|
||||
<p class="has-text-grey fs-12">{{ $dayjs(create_time).format('HH:mm') }}</p>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<button class="button is-dark fs-13 rounded-full">Xác nhận</button>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
<style scoped>
|
||||
td {
|
||||
vertical-align: middle;
|
||||
--bulma-table-cell-padding: 0.75em;
|
||||
}
|
||||
</style>
|
||||
@@ -1,5 +1,54 @@
|
||||
<script setup>
|
||||
import OrderHighlightCard from '@/components/orders/OrderHighlightCard.vue';
|
||||
import OrderPipeline from '@/components/orders/OrderPipeline.vue';
|
||||
import OrdersTable from '@/components/orders/OrdersTable.vue';
|
||||
|
||||
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Đ'
|
||||
},
|
||||
]
|
||||
</script>
|
||||
<template>
|
||||
Orders
|
||||
</template>
|
||||
<div>
|
||||
<div class="fixed-grid has-2-cols-mobile has-5-cols">
|
||||
<div class="grid">
|
||||
<OrderHighlightCard
|
||||
v-for="highlight in highlights"
|
||||
:key="highlight.name"
|
||||
v-bind="highlight"
|
||||
/>
|
||||
</div>
|
||||
<OrderPipeline />
|
||||
<OrdersTable />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
231
app/components/orders/OrdersTable.vue
Normal file
231
app/components/orders/OrdersTable.vue
Normal file
@@ -0,0 +1,231 @@
|
||||
<script setup>
|
||||
import OrderRow from '@/components/orders/OrderRow.vue';
|
||||
|
||||
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: 'orange',
|
||||
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'
|
||||
},
|
||||
]
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<div class="card">
|
||||
<div class="card-content"></div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<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:list-alt-outline-rounded" :size="22" />
|
||||
<span>Danh sách đơn hàng ({{ orders.length }})</span>
|
||||
</p>
|
||||
<table class="table is-fullwidth is-hoverable fs-14">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Đơn hàng</th>
|
||||
<th>Khách hàng</th>
|
||||
<th class="has-text-right">Tổng tiền</th>
|
||||
<th class="has-text-centered">Trạng thái</th>
|
||||
<th>Thanh toán</th>
|
||||
<th>Giao hàng</th>
|
||||
<th>Ngày tạo</th>
|
||||
<th>Thao tác</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<OrderRow
|
||||
v-for="order in orders"
|
||||
:key="order.id"
|
||||
v-bind="order"
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<style scoped>
|
||||
th {
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
44
app/components/orders/PipelinePhase.vue
Normal file
44
app/components/orders/PipelinePhase.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
name: String,
|
||||
value: Number,
|
||||
icon: String,
|
||||
color: String,
|
||||
index: Number,
|
||||
});
|
||||
|
||||
const progressUnfilled = computed(() => `var(--bulma-${props.color}-85)`);
|
||||
</script>
|
||||
<template>
|
||||
<div class="is-flex-grow-1">
|
||||
<div class="is-flex is-justify-content-space-between">
|
||||
<p :class="`has-text-${color}-40`">{{ name }}</p>
|
||||
<p :class="['fs-18 font-bold', `has-text-${color}-30`]">{{ value }}</p>
|
||||
</div>
|
||||
<progress
|
||||
:class="['progress is-small mt-2', `is-${color}`]"
|
||||
value="20"
|
||||
max="100"
|
||||
>
|
||||
</progress>
|
||||
</div>
|
||||
<div
|
||||
v-if="index < 4"
|
||||
class="is-flex is-justify-content-center is-align-items-center"
|
||||
style="width: 60px; height: 48px"
|
||||
>
|
||||
<Icon
|
||||
name="material-symbols:arrow-forward-rounded"
|
||||
:size="24"
|
||||
class="has-text-grey-70"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
$progress-value-background-color: 'red';
|
||||
|
||||
.progress {
|
||||
--bulma-size-small: 0.5rem;
|
||||
background-color: v-bind(progressUnfilled);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user