This commit is contained in:
Viet An
2026-06-18 08:41:33 +07:00
parent a8aa9a3dce
commit b7c82e944d
17 changed files with 531 additions and 775 deletions

View File

@@ -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>

View File

@@ -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 IMEI 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 IMEI 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 {

View File

@@ -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([

View File

@@ -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>

View File

@@ -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),
}"