chore: install prettier

This commit is contained in:
Viet An
2026-05-04 15:22:27 +07:00
parent 93d29ca7d8
commit bd58e2b847
267 changed files with 22950 additions and 13581 deletions

View File

@@ -17,5 +17,4 @@ const props = defineProps({
});
const emit = defineEmits(["remove"]);
</script>

File diff suppressed because it is too large Load Diff

View File

@@ -1,13 +1,22 @@
<template>
<div :id="docid">
<div :id="docid1">
<Caption v-bind="{ title: isVietnamese? 'Thanh toán' : 'Payment', type: 'has-text-warning' }"></Caption>
<Caption
v-bind="{
title: isVietnamese ? 'Thanh toán' : 'Payment',
type: 'has-text-warning',
}"
></Caption>
<div class="columns is-multiline mx-0">
<div class="column is-4 pb-1 px-0">
<div class="field">
<label class="label">{{ dataLang && findFieldName("loan_code")[lang] }}</label>
<div class="control">
<span class="hyperlink" @click="$copyToClipboard(record.code)">{{ record?.code || "/" }}</span>
<span
class="hyperlink"
@click="$copyToClipboard(record.code)"
>{{ record?.code || "/" }}</span
>
</div>
</div>
</div>
@@ -54,7 +63,7 @@
<div class="control">
<span>{{ record?.commission ? $numtoString(record.commission) : "/" }}</span>
</div>
</div>
</div>
</div>
<div class="column is-5 pb-1 px-0">
@@ -76,14 +85,18 @@
</div>
</div>
</div>
</div>
</div>
<!-- <div class="mt-2 border-bottom"></div> -->
<div class="buttons mt-5" id="ignore">
<button class="button is-primary has-text-white mt-2" @click="handleUpdate()">
<div
class="buttons mt-5"
id="ignore"
>
<button
class="button is-primary has-text-white mt-2"
@click="handleUpdate()"
>
{{ dataLang && findFieldName("update")[lang] }}
</button>
</div>

View File

@@ -1,17 +1,28 @@
<template>
<div :id="docid">
<!-- Loading state -->
<div v-if="isLoading" class="has-text-centered mt-5 mb-5" style="min-height: 500px">
<div
v-if="isLoading"
class="has-text-centered mt-5 mb-5"
style="min-height: 500px"
>
<button class="button is-primary is-loading is-large"></button>
<p class="mt-4 has-text-primary has-text-weight-semibold">
{{ isVietnamese ? 'Đang tải hợp đồng...' : 'Loading contracts...' }}
{{ isVietnamese ? "Đang tải hợp đồng..." : "Loading contracts..." }}
</p>
</div>
<!-- No contract state -->
<div v-else-if="!hasContracts" class="has-text-centered mt-5 mb-5" style="min-height: 500px">
<div
v-else-if="!hasContracts"
class="has-text-centered mt-5 mb-5"
style="min-height: 500px"
>
<article class="message is-primary">
<div class="message-body" style="font-size: 17px; text-align: left; color: black">
<div
class="message-body"
style="font-size: 17px; text-align: left; color: black"
>
{{
isVietnamese
? "Chưa có hợp đồng. Vui lòng tạo giao dịch và hợp đồng trước."
@@ -24,10 +35,22 @@
<!-- Contracts list -->
<template v-else>
<!-- Tabs khi nhiều hợp đồng -->
<div class="tabs border-bottom" id="ignore" v-if="contractsList.length > 1">
<div
class="tabs border-bottom"
id="ignore"
v-if="contractsList.length > 1"
>
<ul class="tabs-list">
<li class="tabs-item" style="border: none" v-for="(contract, index) in contractsList" :key="index"
:class="{ 'bg-primary has-text-white': activeContractIndex === index }" @click="switchContract(index)">
<li
class="tabs-item"
style="border: none"
v-for="(contract, index) in contractsList"
:key="index"
:class="{
'bg-primary has-text-white': activeContractIndex === index,
}"
@click="switchContract(index)"
>
<a class="tabs-link">
<span>{{ contract.document[0]?.name || contract.document[0]?.en || `Contract ${index + 1}` }}</span>
</a>
@@ -38,23 +61,42 @@
<!-- Contract content -->
<div v-if="currentContract && pdfFileUrl && hasValidDocument">
<div class="contract-content mt-2">
<iframe :src="`https://mozilla.github.io/pdf.js/web/viewer.html?file=${pdfFileUrl}`" width="100%"
height="90vh" scrolling="no" style="border: none; height: 75vh; top: 0; left: 0; right: 0; bottom: 0">
<iframe
:src="`https://mozilla.github.io/pdf.js/web/viewer.html?file=${pdfFileUrl}`"
width="100%"
height="90vh"
scrolling="no"
style="border: none; height: 75vh; top: 0; left: 0; right: 0; bottom: 0"
>
</iframe>
</div>
</div>
<!-- Download buttons -->
<div class="mt-4" id="ignore">
<button v-if="hasValidDocument" class="button is-primary has-text-white mr-4" @click="downloadDocx">
<div
class="mt-4"
id="ignore"
>
<button
v-if="hasValidDocument"
class="button is-primary has-text-white mr-4"
@click="downloadDocx"
>
{{ isVietnamese ? "Tải file docx" : "Download contract as docx" }}
</button>
<button v-if="hasValidDocument" class="button is-primary has-text-white mr-4" @click="downloadPdf">
<button
v-if="hasValidDocument"
class="button is-primary has-text-white mr-4"
@click="downloadPdf"
>
{{ isVietnamese ? "Tải file pdf" : "Download contract as pdf" }}
</button>
<p v-if="contractError" class="has-text-danger mt-2">
<p
v-if="contractError"
class="has-text-danger mt-2"
>
{{ contractError }}
</p>
</div>
@@ -73,16 +115,16 @@ export default {
props: {
contractId: {
type: [Number, String],
default: null
default: null,
},
row: {
type: Object,
default: null
default: null,
},
directDocument: {
type: Object,
default: null
}
default: null,
},
},
emits: ["contractCreated", "update", "close", "dataevent"],
data() {
@@ -107,10 +149,12 @@ export default {
},
hasValidDocument() {
if (!this.currentContract) return false;
return this.currentContract.document &&
return (
this.currentContract.document &&
this.currentContract.document.length > 0 &&
this.currentContract.document[0]?.pdf;
}
this.currentContract.document[0]?.pdf
);
},
},
async created() {
try {
@@ -118,11 +162,9 @@ export default {
this.contractError = null;
if (this.directDocument) {
this.contractsList = [
{ document: [this.directDocument] }
];
this.contractsList = [{ document: [this.directDocument] }];
this.updatePdfUrl(0);
return;
return;
}
let contracts = [];
@@ -130,31 +172,22 @@ export default {
if (this.contractId) {
fetchParams = { id: this.contractId };
}
else if (this.row?.id) {
} else if (this.row?.id) {
fetchParams = { transaction: this.row.id };
}
if (!fetchParams) {
throw new Error(
this.isVietnamese
? 'Không có ID hợp đồng hoặc transaction để tải.'
: 'No contract ID or transaction provided to load.'
? "Không có ID hợp đồng hoặc transaction để tải."
: "No contract ID or transaction provided to load.",
);
}
contracts = await this.$getdata(
'contract',
fetchParams,
undefined
);
contracts = await this.$getdata("contract", fetchParams, undefined);
if (!contracts || contracts.length === 0) {
throw new Error(
this.isVietnamese
? 'Không tìm thấy hợp đồng.'
: 'Contract not found.'
);
throw new Error(this.isVietnamese ? "Không tìm thấy hợp đồng." : "Contract not found.");
}
this.contractsList = contracts;
@@ -164,12 +197,9 @@ export default {
this.updatePdfUrl(this.activeContractIndex);
}
} catch (error) {
console.error('Error loading contracts:', error);
this.contractError = error.message || (
this.isVietnamese
? 'Lỗi khi tải danh sách hợp đồng.'
: 'Error loading contracts list.'
);
console.error("Error loading contracts:", error);
this.contractError =
error.message || (this.isVietnamese ? "Lỗi khi tải danh sách hợp đồng." : "Error loading contracts list.");
this.contractsList = [];
} finally {
this.isLoading = false;
@@ -192,10 +222,7 @@ export default {
downloadDocx() {
if (!this.hasValidDocument) {
this.$snackbar(
this.isVietnamese ? "Không có file để tải" : "No file to download",
{ type: 'is-warning' }
);
this.$snackbar(this.isVietnamese ? "Không có file để tải" : "No file to download", { type: "is-warning" });
return;
}
@@ -206,10 +233,7 @@ export default {
downloadPdf() {
if (!this.hasValidDocument) {
this.$snackbar(
this.isVietnamese ? "Không có file để tải" : "No file to download",
{ type: 'is-warning' }
);
this.$snackbar(this.isVietnamese ? "Không có file để tải" : "No file to download", { type: "is-warning" });
return;
}
@@ -259,4 +283,4 @@ export default {
max-width: 100%;
}
}
</style>
</style>

View File

@@ -1,17 +1,29 @@
<template>
<div>
<!-- Loading Overlay -->
<div v-if="isLoading" class="loading-overlay">
<div
v-if="isLoading"
class="loading-overlay"
>
<div class="loader"></div>
</div>
<!-- View Mode Toggle -->
<div class="mb-5 pb-3" style="border-bottom: 2px solid #e8e8e8;">
<div class="buttons has-addons ">
<button @click="viewMode = 'list'" :class="['button', viewMode === 'list' ? 'is-primary' : 'is-light']">
<div
class="mb-5 pb-3"
style="border-bottom: 2px solid #e8e8e8"
>
<div class="buttons has-addons">
<button
@click="viewMode = 'list'"
:class="['button', viewMode === 'list' ? 'is-primary' : 'is-light']"
>
Danh sách
</button>
<button @click="viewMode = 'gallery'" :class="['button', viewMode === 'gallery' ? 'is-primary' : 'is-light']">
<button
@click="viewMode = 'gallery'"
:class="['button', viewMode === 'gallery' ? 'is-primary' : 'is-light']"
>
Thư viện
</button>
</div>
@@ -19,8 +31,11 @@
<!-- Phase Document Types List -->
<div v-if="phasedoctypes && phasedoctypes.length > 0">
<div v-for="doctype in phasedoctypes" :key="doctype.id" class="mb-6">
<div
v-for="doctype in phasedoctypes"
:key="doctype.id"
class="mb-6"
>
<!-- Document Type Header with Upload Button -->
<div class="level is-mobile mb-4">
<div class="level-left">
@@ -34,9 +49,9 @@
<div class="level-item">
<FileUpload
v-if="$getEditRights()"
:type="['file', 'image', 'pdf']"
@files="(files) => handleUpload(files, doctype.doctype)"
position="right"
:type="['file', 'image', 'pdf']"
@files="(files) => handleUpload(files, doctype.doctype)"
position="right"
/>
</div>
</div>
@@ -45,112 +60,195 @@
<!-- List View -->
<div v-if="viewMode === 'list'">
<div v-if="getFilesByDocType(doctype.doctype).length > 0">
<div v-for="file in getFilesByDocType(doctype.doctype)" :key="file.id"
<div
v-for="file in getFilesByDocType(doctype.doctype)"
:key="file.id"
class="is-flex is-justify-content-space-between is-align-items-center py-3 px-4 has-background-warning has-text-white"
style="border-bottom: #e8e8e8 solid 1px; transition: all 0.2s ease; opacity: 0.95; cursor: pointer;"
style="border-bottom: #e8e8e8 solid 1px; transition: all 0.2s ease; opacity: 0.95; cursor: pointer"
@mouseenter="$event.currentTarget.style.opacity = '1'"
@mouseleave="$event.currentTarget.style.opacity = '0.95'">
<div style="flex: 1; min-width: 0;">
<p class="is-size-7 has-text-weight-semibold has-text-white mb-1" style="word-break: break-word;">
@mouseleave="$event.currentTarget.style.opacity = '0.95'"
>
<div style="flex: 1; min-width: 0">
<p
class="is-size-7 has-text-weight-semibold has-text-white mb-1"
style="word-break: break-word"
>
{{ file.name || file.file__name }}
</p>
<p class="is-size-7 has-text-white-bis">
{{ $formatFileSize(file.file__size) }} {{ $dayjs(file.create_time).format("DD/MM/YYYY HH:mm") }}
{{ $formatFileSize(file.file__size) }}
{{ $dayjs(file.create_time).format("DD/MM/YYYY HH:mm") }}
</p>
</div>
<div class="buttons are-small ml-3">
<button @click="viewFile(file)" class="button has-background-white has-text-primary ">
<button
@click="viewFile(file)"
class="button has-background-white has-text-primary"
>
<span class="icon">
<SvgIcon v-bind="{
name: 'view.svg',
type: 'success',
size: 18,
}"></SvgIcon>
<SvgIcon
v-bind="{
name: 'view.svg',
type: 'success',
size: 18,
}"
></SvgIcon>
</span>
</button>
<button @click="downloadFile(file)" class="button has-background-white has-text-primary ">
<button
@click="downloadFile(file)"
class="button has-background-white has-text-primary"
>
<span class="icon">
<SvgIcon v-bind="{
name: 'download.svg',
type: 'success',
size: 18,
}"></SvgIcon>
<SvgIcon
v-bind="{
name: 'download.svg',
type: 'success',
size: 18,
}"
></SvgIcon>
</span>
</button>
<button @click="deleteFile(file.id)" class="button has-background-white has-text-danger ">
<button
@click="deleteFile(file.id)"
class="button has-background-white has-text-danger"
>
<span class="icon">
<SvgIcon v-bind="{
name: 'bin.svg',
type: 'danger',
size: 18,
}"></SvgIcon>
<SvgIcon
v-bind="{
name: 'bin.svg',
type: 'danger',
size: 18,
}"
></SvgIcon>
</span>
</button>
</div>
</div>
</div>
<div v-else class="has-text-grey-light is-size-7 has-text-centered py-5">
<div
v-else
class="has-text-grey-light is-size-7 has-text-centered py-5"
>
Chưa có file nào
</div>
</div>
<!-- Gallery View -->
<div v-if="viewMode === 'gallery'">
<div v-if="getFilesByDocType(doctype.doctype).length > 0" class="columns is-multiline is-variable is-2">
<div v-for="file in getFilesByDocType(doctype.doctype)" :key="file.id"
class="column is-half-tablet is-one-third-desktop">
<div class="has-background-warning has-text-white"
style="border-radius: 6px; overflow: hidden; height: 100%; display: flex; flex-direction: column; transition: all 0.3s ease; box-shadow: 0 2px 8px rgba(50, 115, 220, 0.2);"
@mouseenter="$event.currentTarget.style.transform = 'translateY(-4px)'; $event.currentTarget.style.boxShadow = '0 6px 16px rgba(50, 115, 220, 0.3)'"
@mouseleave="$event.currentTarget.style.transform = 'translateY(0)'; $event.currentTarget.style.boxShadow = '0 2px 8px rgba(50, 115, 220, 0.2)'">
<div
v-if="getFilesByDocType(doctype.doctype).length > 0"
class="columns is-multiline is-variable is-2"
>
<div
v-for="file in getFilesByDocType(doctype.doctype)"
:key="file.id"
class="column is-half-tablet is-one-third-desktop"
>
<div
class="has-background-warning has-text-white"
style="
border-radius: 6px;
overflow: hidden;
height: 100%;
display: flex;
flex-direction: column;
transition: all 0.3s ease;
box-shadow: 0 2px 8px rgba(50, 115, 220, 0.2);
"
@mouseenter="
$event.currentTarget.style.transform = 'translateY(-4px)';
$event.currentTarget.style.boxShadow = '0 6px 16px rgba(50, 115, 220, 0.3)';
"
@mouseleave="
$event.currentTarget.style.transform = 'translateY(0)';
$event.currentTarget.style.boxShadow = '0 2px 8px rgba(50, 115, 220, 0.2)';
"
>
<div
style="flex: 1; display: flex; align-items: center; justify-content: center; padding: 16px; background: rgba(255, 255, 255, 0.1); min-height: 140px;">
<div v-if="isImage(file.file__name)"
style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;">
<img :src="`${$getpath()}static/files/${file.file__file}`" :alt="file.file__name"
style="max-width: 100%; max-height: 100%; object-fit: contain;">
style="
flex: 1;
display: flex;
align-items: center;
justify-content: center;
padding: 16px;
background: rgba(255, 255, 255, 0.1);
min-height: 140px;
"
>
<div
v-if="isImage(file.file__name)"
style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center"
>
<img
:src="`${$getpath()}static/files/${file.file__file}`"
:alt="file.file__name"
style="max-width: 100%; max-height: 100%; object-fit: contain"
/>
</div>
<div v-else class="has-text-white-ter" style="font-size: 48px; line-height: 1;">
<div
v-else
class="has-text-white-ter"
style="font-size: 48px; line-height: 1"
>
FILE
</div>
</div>
<div style="padding: 12px 16px;">
<p class="is-size-7 has-text-weight-semibold has-text-white mb-1" :title="file.file__name"
style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
<div style="padding: 12px 16px">
<p
class="is-size-7 has-text-weight-semibold has-text-white mb-1"
:title="file.file__name"
style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis"
>
{{ file.file__name }}
</p>
<p class="is-size-7 has-text-white-bis mb-3">{{ $formatFileSize(file.file__size) }}</p>
<p class="is-size-7 has-text-white-bis mb-3">
{{ $formatFileSize(file.file__size) }}
</p>
<div class="buttons are-small is-centered">
<button @click="viewFile(file)" class="button has-background-white has-text-primary ">
<button
@click="viewFile(file)"
class="button has-background-white has-text-primary"
>
<span class="icon">
<SvgIcon v-bind="{
name: 'view.svg',
type: 'success',
size: 18,
}"></SvgIcon>
<SvgIcon
v-bind="{
name: 'view.svg',
type: 'success',
size: 18,
}"
></SvgIcon>
</span>
</button>
<button @click="downloadFile(file)" class="button has-background-white has-text-primary ">
<button
@click="downloadFile(file)"
class="button has-background-white has-text-primary"
>
<span class="icon">
<SvgIcon v-bind="{
name: 'download.svg',
type: 'success',
size: 18,
}"></SvgIcon>
<SvgIcon
v-bind="{
name: 'download.svg',
type: 'success',
size: 18,
}"
></SvgIcon>
</span>
</button>
<button @click="deleteFile(file.id)" class="button has-background-white has-text-danger ">
<button
@click="deleteFile(file.id)"
class="button has-background-white has-text-danger"
>
<span class="icon">
<SvgIcon v-bind="{
name: 'bin.svg',
type: 'danger',
size: 18,
}"></SvgIcon>
<SvgIcon
v-bind="{
name: 'bin.svg',
type: 'danger',
size: 18,
}"
></SvgIcon>
</span>
</button>
</div>
@@ -158,7 +256,10 @@
</div>
</div>
</div>
<div v-else class="has-text-grey-light is-size-7 has-text-centered py-5">
<div
v-else
class="has-text-grey-light is-size-7 has-text-centered py-5"
>
Chưa có file nào
</div>
</div>
@@ -166,11 +267,19 @@
</div>
<!-- If no phase doctypes -->
<div v-else-if="!isLoading" class="has-text-centered py-6">
<div
v-else-if="!isLoading"
class="has-text-centered py-6"
>
<p class="has-text-grey-light is-size-7">Chưa có loại tài liệu được định nghĩa cho giai đoạn này.</p>
</div>
<Modal @close="showmodal = undefined" @modalevent="handleModalEvent" v-bind="showmodal" v-if="showmodal"></Modal>
<Modal
@close="showmodal = undefined"
@modalevent="handleModalEvent"
v-bind="showmodal"
v-if="showmodal"
></Modal>
</div>
</template>
@@ -184,7 +293,7 @@ export default {
props: {
row: {
type: Object,
required: true
required: true,
},
},
data() {
@@ -194,7 +303,7 @@ export default {
isLoading: false,
showmodal: undefined,
phasedoctypes: [],
viewMode: 'list',
viewMode: "list",
};
},
async created() {
@@ -221,9 +330,14 @@ export default {
if (!this.transaction?.phase) return;
try {
const phasedoctypesData = await $getdata('phasedoctype', {
phase: this.transaction.phase,
}, undefined, false);
const phasedoctypesData = await $getdata(
"phasedoctype",
{
phase: this.transaction.phase,
},
undefined,
false,
);
if (phasedoctypesData) {
this.phasedoctypes = Array.isArray(phasedoctypesData) ? phasedoctypesData : [phasedoctypesData];
@@ -240,15 +354,27 @@ export default {
if (!this.row.id) return;
this.isLoading = true;
try {
const detail = await $getdata('reservation', {
id: this.transaction.txncurrent__detail
}, undefined, true)
const filesArray = await $getdata('transactionfile', {
txn_detail: detail.id,
}, undefined, false);
const detail = await $getdata(
"reservation",
{
id: this.transaction.txncurrent__detail,
},
undefined,
true,
);
const filesArray = await $getdata(
"transactionfile",
{
txn_detail: detail.id,
},
undefined,
false,
);
if (filesArray) {
this.files = (Array.isArray(filesArray) ? filesArray : [filesArray]).sort((a, b) => new Date(b.create_time) - new Date(a.create_time));
this.files = (Array.isArray(filesArray) ? filesArray : [filesArray]).sort(
(a, b) => new Date(b.create_time) - new Date(a.create_time),
);
} else {
this.files = [];
}
@@ -260,18 +386,20 @@ export default {
}
},
getFilesByDocType(docTypeId) {
return this.files.filter(file => file.file__doc_type === docTypeId || (file.file__doc_type == null && docTypeId == null));
return this.files.filter(
(file) => file.file__doc_type === docTypeId || (file.file__doc_type == null && docTypeId == null),
);
},
getFileExtension(fileName) {
return fileName ? fileName.split('.').pop().toLowerCase() : 'file';
return fileName ? fileName.split(".").pop().toLowerCase() : "file";
},
isImage(fileName) {
const ext = this.getFileExtension(fileName);
return ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].includes(ext);
return ["jpg", "jpeg", "png", "gif", "bmp", "webp"].includes(ext);
},
isViewableDocument(fileName) {
const ext = this.getFileExtension(fileName);
return ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx'].includes(ext);
return ["pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx"].includes(ext);
},
async handleUpload(uploadedFiles, docTypeId) {
if (!uploadedFiles || uploadedFiles.length === 0) return;
@@ -282,15 +410,20 @@ export default {
try {
for (const fileRecord of uploadedFiles) {
if (docTypeId) {
await $patchapi('file', {
await $patchapi("file", {
id: fileRecord.id,
doc_type: docTypeId
doc_type: docTypeId,
});
}
const detail = await $getdata('reservation', {
id: this.transaction.txncurrent__detail,
}, undefined, true)
const detail = await $getdata(
"reservation",
{
id: this.transaction.txncurrent__detail,
},
undefined,
true,
);
const payload = {
txn_detail: detail.id,
@@ -305,7 +438,7 @@ export default {
}
await this.fetchFiles();
this.$emit('upload-completed');
this.$emit("upload-completed");
} catch (error) {
console.error("Lỗi khi lưu file:", error);
alert("Đã xảy ra lỗi khi tải file lên. Vui lòng thử lại.");
@@ -318,21 +451,21 @@ export default {
const filePath = file.file__file || file.file;
if (!filePath) return;
const link = document.createElement('a');
const link = document.createElement("a");
link.href = `${$getpath()}static/files/${filePath}`;
link.download = file.file__name || 'download';
link.download = file.file__name || "download";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
},
deleteFile(fileId) {
this.showmodal = {
component: 'dialog/Confirm',
title: 'Xác nhận xóa',
height: '10vh',
width: '40%',
component: "dialog/Confirm",
title: "Xác nhận xóa",
height: "10vh",
width: "40%",
vbind: {
content: 'Bạn có chắc chắn muốn xóa file này không?'
content: "Bạn có chắc chắn muốn xóa file này không?",
},
onConfirm: async () => {
this.isLoading = true;
@@ -350,17 +483,17 @@ export default {
} finally {
this.isLoading = false;
}
}
},
};
},
handleModalEvent(event) {
if (event.name === 'confirm' && typeof this.showmodal?.onConfirm === 'function') {
if (event.name === "confirm" && typeof this.showmodal?.onConfirm === "function") {
this.showmodal.onConfirm();
}
},
viewFile(file) {
const { $getpath } = useNuxtApp();
const fileName = file.file__name || '';
const fileName = file.file__name || "";
const filePath = file.file__file || file.file;
if (!filePath) return;
@@ -371,23 +504,23 @@ export default {
if (isImageFile) {
this.showmodal = {
title: fileName,
component: 'media/ChipImage',
component: "media/ChipImage",
vbind: {
extend: false,
file: file,
image: fileUrl,
show: ['download', 'delete']
}
show: ["download", "delete"],
},
};
} else if (isViewable) {
// Mở Google Viewer trực tiếp trong tab mới
const viewerUrl = `https://docs.google.com/gview?url=${fileUrl}&embedded=false`;
window.open(viewerUrl, '_blank');
window.open(viewerUrl, "_blank");
} else {
this.downloadFile(file);
}
},
}
},
};
</script>
@@ -420,4 +553,4 @@ export default {
transform: rotate(360deg);
}
}
</style>
</style>

View File

@@ -1,4 +1,4 @@
<!-- Viewer: display when click tem from another dealer -->
<template>
<p>Rất tiếc, bạn hiện chưa quyền xem thông tin sản phẩm này.</p>
</template>
</template>

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,15 @@
<template>
<div v-if="productData" class="grid px-3">
<div
v-if="productData"
class="grid px-3"
>
<div class="cell is-col-span-12">
<div id="schedule-content">
<div v-if="selectedPolicy" id="print-area" :class="{ 'is-loading': isLoading }">
<div
v-if="selectedPolicy"
id="print-area"
:class="{ 'is-loading': isLoading }"
>
<!-- Header -->
<div class="is-flex is-justify-content-space-between is-align-items-center">
<h3 class="title is-4 has-text-primary mb-1">
@@ -12,101 +19,186 @@
<span class="button is-white">
<span class="has-text-weight-semibold">Đơn vị: VNĐ</span>
</span>
<button class="button is-light" @click="$emit('print')" id="ignore-print">
<button
class="button is-light"
@click="$emit('print')"
id="ignore-print"
>
<span class="is-size-6">In</span>
</button>
</div>
</div>
<hr class="my-4" style="background-color: var(--bulma-background)"></hr>
<hr
class="my-4"
style="background-color: var(--bulma-background)"
/>
<!-- Summary Information -->
<div class="fixed-grid has-4-cols-mobile has-7-cols-desktop">
<div class="grid">
<div class="cell is-col-span-6-mobile is-col-span-1-desktop">
<p class="is-size-6 has-text-weight-bold mb-1">Sản phẩm</p>
<p class="has-text-primary has-text-weight-medium">{{ productData.trade_code || productData.code }} <a
class="ml-4" id="ignore" @click="$copyToClipboard(productData.trade_code)">
<SvgIcon name="copy.svg" type="primary" :size="18" />
<p class="has-text-primary has-text-weight-medium">
{{ productData.trade_code || productData.code }}
<a
class="ml-4"
id="ignore"
@click="$copyToClipboard(productData.trade_code)"
>
<SvgIcon
name="copy.svg"
type="primary"
:size="18"
/>
</a>
</p>
</div>
<div class="cell is-col-span-6-mobile is-col-span-1-desktop">
<p class="is-size-6 has-text-weight-bold mb-1">Giá niêm yết</p>
<p class="has-text-primary">{{ $numtoString(calculatorData.originPrice) }}</p>
<p class="has-text-primary">
{{ $numtoString(calculatorData.originPrice) }}
</p>
</div>
<div class="cell is-col-span-6-mobile is-col-span-1-desktop">
<p class="is-size-6 has-text-weight-bold mb-1">Tổng chiết khấu</p>
<p class="has-text-danger has-text-weight-bold">{{ $numtoString(calculatorData.totalDiscount) }}</p>
<p class="has-text-danger has-text-weight-bold">
{{ $numtoString(calculatorData.totalDiscount) }}
</p>
</div>
<div class="cell is-col-span-6-mobile is-col-span-1-desktop">
<p class="is-size-6 has-text-weight-bold mb-1">Giá sau chiết khấu</p>
<p class="has-text-black has-text-weight-bold">{{ $numtoString(calculatorData.salePrice) }}</p>
<p class="has-text-black has-text-weight-bold">
{{ $numtoString(calculatorData.salePrice) }}
</p>
</div>
<div v-if="selectedPolicy.contract_allocation_percentage < 100"
class="cell is-col-span-6-mobile is-col-span-1-desktop">
<div
v-if="selectedPolicy.contract_allocation_percentage < 100"
class="cell is-col-span-6-mobile is-col-span-1-desktop"
>
<p class="is-size-6 has-text-weight-bold mb-1">Giá trị bảo đảm</p>
<p class="has-text-primary">{{ $numtoString(calculatorData.allocatedPrice) }}</p>
<p class="has-text-primary">
{{ $numtoString(calculatorData.allocatedPrice) }}
</p>
</div>
<div v-if="totalPaid === 0" class="cell is-col-span-6-mobile is-col-span-1-desktop">
<div
v-if="totalPaid === 0"
class="cell is-col-span-6-mobile is-col-span-1-desktop"
>
<p class="is-size-6 has-text-weight-bold mb-1">Đặt cọc</p>
<p class="has-text-primary">{{ $numtoString(selectedPolicy.deposit) }}</p>
<p class="has-text-primary">
{{ $numtoString(selectedPolicy.deposit) }}
</p>
</div>
<div class="cell is-col-span-6-mobile is-col-span-1-desktop">
<p class="is-size-6 has-text-weight-bold mb-1">Khách hàng</p>
<p v-if="selectedCustomer" class="has-text-primary has-text-weight-medium">
<p
v-if="selectedCustomer"
class="has-text-primary has-text-weight-medium"
>
{{ selectedCustomer.code }} - {{ selectedCustomer.fullname }}
</p>
<p v-else class="has-text-grey is-italic is-size-6">Chưa chọn</p>
<p
v-else
class="has-text-grey is-italic is-size-6"
>
Chưa chọn
</p>
</div>
</div>
</div>
<hr class="my-4" style="background-color: var(--bulma-background)"></hr>
<hr
class="my-4"
style="background-color: var(--bulma-background)"
/>
<!-- Detailed Discounts -->
<div v-if="calculatorData.detailedDiscounts && calculatorData.detailedDiscounts.length > 0" class="mt-4 mb-4">
<p class="has-text-weight-bold is-size-5 mb-2 has-text-primary is-underlined">
CHI TIẾT CHIẾT KHẤU:
</p>
<div
v-if="calculatorData.detailedDiscounts && calculatorData.detailedDiscounts.length > 0"
class="mt-4 mb-4"
>
<p class="has-text-weight-bold is-size-5 mb-2 has-text-primary is-underlined">CHI TIẾT CHIẾT KHẤU:</p>
<table class="table is-fullwidth is-hoverable is-size-6">
<thead>
<tr>
<th class="has-background-primary has-text-white has-font-weight-normal" style="border: none;"
colspan="2">Diễn giải chiết khấu</th>
<th class="has-background-primary has-text-white has-font-weight-normal has-text-right"
style="border: none;" width="15%">Giá trị</th>
<th class="has-background-primary has-text-white has-font-weight-normal has-text-right"
style="border: none;" width="20%">Thành tiền</th>
<th class="has-background-primary has-text-white has-font-weight-normal has-text-right"
style="border: none;" width="20%">Còn lại</th>
<th
class="has-background-primary has-text-white has-font-weight-normal"
style="border: none"
colspan="2"
>
Diễn giải chiết khấu
</th>
<th
class="has-background-primary has-text-white has-font-weight-normal has-text-right"
style="border: none"
width="15%"
>
Giá trị
</th>
<th
class="has-background-primary has-text-white has-font-weight-normal has-text-right"
style="border: none"
width="20%"
>
Thành tiền
</th>
<th
class="has-background-primary has-text-white has-font-weight-normal has-text-right"
style="border: none"
width="20%"
>
Còn lại
</th>
</tr>
</thead>
<tbody>
<tr style="border-bottom: 1px solid #f5f5f5;" class="has-text-grey-light">
<td colspan="4" class="has-text-right pt-1 pb-1">Giá gốc</td>
<td class="has-text-right has-text-weight-bold pt-1 pb-1">{{
$numtoString(calculatorData.originPrice) }}</td>
<tr
style="border-bottom: 1px solid #f5f5f5"
class="has-text-grey-light"
>
<td
colspan="4"
class="has-text-right pt-1 pb-1"
>
Giá gốc
</td>
<td class="has-text-right has-text-weight-bold pt-1 pb-1">
{{ $numtoString(calculatorData.originPrice) }}
</td>
</tr>
<tr v-for="(item, idx) in calculatorData.detailedDiscounts" :key="`discount-${idx}`"
style="border-bottom: 1px solid #f5f5f5;">
<td width="5%" class="has-text-centered">{{ idx + 1 }}</td>
<tr
v-for="(item, idx) in calculatorData.detailedDiscounts"
:key="`discount-${idx}`"
style="border-bottom: 1px solid #f5f5f5"
>
<td
width="5%"
class="has-text-centered"
>
{{ idx + 1 }}
</td>
<td>
<span class="has-text-weight-semibold">{{ item.name }}</span>
<span class="tag is-primary has-text-white is-rounded border ml-1">{{ item.code }}</span>
</td>
<td class="has-text-right">{{ item.customType === 1 ? item.customValue + '%' :
$numtoString(item.customValue) }}</td>
<td class="has-text-right">
{{ item.customType === 1 ? item.customValue + "%" : $numtoString(item.customValue) }}
</td>
<td class="has-text-right has-text-danger">-{{ $numtoString(item.amount) }}</td>
<td class="has-text-right has-text-primary">{{ $numtoString(item.remaining) }}</td>
<td class="has-text-right has-text-primary">
{{ $numtoString(item.remaining) }}
</td>
</tr>
</tbody>
</table>
</div>
<!-- Early Payment Details -->
<div v-if="isEarlyPaymentActive" class="mt-4 mb-4">
<div
v-if="isEarlyPaymentActive"
class="mt-4 mb-4"
>
<!-- Original Schedule -->
<p class="has-text-weight-bold is-size-5 mb-2 has-text-primary is-underlined">
LỊCH THANH TOÁN GỐC (THEO CHÍNH SÁCH)
@@ -115,26 +207,57 @@
<table class="table is-fullwidth is-hoverable is-size-6">
<thead>
<tr>
<th class="has-background-primary has-text-white has-font-weight-normal" style="border: none;">Đợt
<th
class="has-background-primary has-text-white has-font-weight-normal"
style="border: none"
>
Đợt
</th>
<th
class="has-background-primary has-text-white has-font-weight-normal has-text-right"
style="border: none"
>
Tỷ lệ
</th>
<th
class="has-background-primary has-text-white has-font-weight-normal has-text-right"
style="border: none"
>
Số tiền (VND)
</th>
<th
class="has-background-primary has-text-white has-font-weight-normal"
style="border: none"
>
Ngày bắt đầu
</th>
<th
class="has-background-primary has-text-white has-font-weight-normal"
style="border: none"
>
Ngày đến hạn
</th>
<th
class="has-background-primary has-text-white has-font-weight-normal has-text-right"
style="border: none"
>
Số ngày
</th>
<th class="has-background-primary has-text-white has-font-weight-normal has-text-right"
style="border: none;">Tỷ lệ</th>
<th class="has-background-primary has-text-white has-font-weight-normal has-text-right"
style="border: none;">Số tiền (VND)</th>
<th class="has-background-primary has-text-white has-font-weight-normal" style="border: none;">Ngày
bắt đầu</th>
<th class="has-background-primary has-text-white has-font-weight-normal" style="border: none;">Ngày
đến hạn</th>
<th class="has-background-primary has-text-white has-font-weight-normal has-text-right"
style="border: none;">Số ngày</th>
</tr>
</thead>
<tbody>
<tr v-for="(plan, index) in calculatorData.originalPaymentSchedule" :key="`orig-plan-${index}`"
style="border-bottom: 1px solid #f5f5f5;">
<tr
v-for="(plan, index) in calculatorData.originalPaymentSchedule"
:key="`orig-plan-${index}`"
style="border-bottom: 1px solid #f5f5f5"
>
<td class="has-text-weight-semibold">Đợt {{ plan.cycle }}</td>
<td class="has-text-right">{{ plan.type === 1 ? `${plan.value}%` : '-' }}</td>
<td class="has-text-right">{{ $numtoString(plan.amount) }}</td>
<td class="has-text-right">
{{ plan.type === 1 ? `${plan.value}%` : "-" }}
</td>
<td class="has-text-right">
{{ $numtoString(plan.amount) }}
</td>
<td>{{ formatDate(plan.from_date) }}</td>
<td>{{ formatDate(plan.to_date) }}</td>
<td class="has-text-right">{{ plan.days }}</td>
@@ -151,29 +274,62 @@
<table class="table is-fullwidth is-hoverable is-size-6">
<thead>
<tr>
<th class="has-background-primary has-text-white has-font-weight-normal" style="border: none;">Đợt
<th
class="has-background-primary has-text-white has-font-weight-normal"
style="border: none"
>
Đợt
</th>
<th
class="has-background-primary has-text-white has-font-weight-normal"
style="border: none"
>
Hạn TT Gốc
</th>
<th
class="has-background-primary has-text-white has-font-weight-normal"
style="border: none"
>
Ngày TT Thực Tế
</th>
<th
class="has-background-primary has-text-white has-font-weight-normal has-text-right"
style="border: none"
>
Số tiền gốc
</th>
<th
class="has-background-primary has-text-white has-font-weight-normal has-text-right"
style="border: none"
>
Số ngày TT sớm
</th>
<th
class="has-background-primary has-text-white has-font-weight-normal has-text-right"
style="border: none"
>
Tỷ lệ CK (%/ngày)
</th>
<th
class="has-background-primary has-text-white has-font-weight-normal has-text-right"
style="border: none"
>
Tiền chiết khấu
</th>
<th class="has-background-primary has-text-white has-font-weight-normal" style="border: none;">Hạn
TT Gốc</th>
<th class="has-background-primary has-text-white has-font-weight-normal" style="border: none;">Ngày
TT Thực Tế</th>
<th class="has-background-primary has-text-white has-font-weight-normal has-text-right"
style="border: none;">Số tiền gốc</th>
<th class="has-background-primary has-text-white has-font-weight-normal has-text-right"
style="border: none;">Số ngày TT sớm</th>
<th class="has-background-primary has-text-white has-font-weight-normal has-text-right"
style="border: none;">Tỷ lệ CK (%/ngày)</th>
<th class="has-background-primary has-text-white has-font-weight-normal has-text-right"
style="border: none;">Tiền chiết khấu</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, idx) in calculatorData.earlyDiscountDetails" :key="`early-discount-${idx}`"
style="border-bottom: 1px solid #f5f5f5;">
<tr
v-for="(item, idx) in calculatorData.earlyDiscountDetails"
:key="`early-discount-${idx}`"
style="border-bottom: 1px solid #f5f5f5"
>
<td>Đợt {{ item.cycle }}</td>
<td>{{ formatDate(item.original_payment_date) }}</td>
<td>{{ formatDate(item.actual_payment_date) }}</td>
<td class="has-text-right">{{ $numtoString(item.original_amount) }}</td>
<td class="has-text-right">
{{ $numtoString(item.original_amount) }}
</td>
<td class="has-text-right">{{ item.early_days }}</td>
<td class="has-text-right">{{ item.discount_rate }}</td>
<td class="has-text-right has-text-danger">-{{ $numtoString(item.discount_amount) }}</td>
@@ -181,9 +337,15 @@
</tbody>
<tfoot>
<tr class="has-background-light">
<th colspan="6" class="has-text-right has-text-weight-bold">Tổng chiết khấu thanh toán sớm</th>
<th class="has-text-right has-text-weight-bold has-text-danger">-{{
$numtoString(totalEarlyDiscount) }}</th>
<th
colspan="6"
class="has-text-right has-text-weight-bold"
>
Tổng chiết khấu thanh toán sớm
</th>
<th class="has-text-right has-text-weight-bold has-text-danger">
-{{ $numtoString(totalEarlyDiscount) }}
</th>
</tr>
</tfoot>
</table>
@@ -191,7 +353,10 @@
</div>
<!-- Payment Schedule Table -->
<div v-if="displaySchedule.length > 0" class="mt-4">
<div
v-if="displaySchedule.length > 0"
class="mt-4"
>
<div class="level m-0 mb-2 is-mobile">
<div class="level-left">
<p class="has-text-weight-bold is-size-5 has-text-primary is-underlined">
@@ -199,14 +364,23 @@
<span v-else>LỊCH THANH TOÁN</span>
</p>
</div>
<div class="level-right" id="ignore-print">
<div
class="level-right"
id="ignore-print"
>
<div class="buttons are-small has-addons">
<button class="button" @click="viewMode = 'table'"
:class="viewMode === 'table' ? 'is-link is-selected' : 'is-light'">
<button
class="button"
@click="viewMode = 'table'"
:class="viewMode === 'table' ? 'is-link is-selected' : 'is-light'"
>
<span class="is-size-6">Bảng</span>
</button>
<button class="button" @click="viewMode = 'list'"
:class="viewMode === 'list' ? 'is-link is-selected' : 'is-light'">
<button
class="button"
@click="viewMode = 'list'"
:class="viewMode === 'list' ? 'is-link is-selected' : 'is-light'"
>
<span class="is-size-6">Thẻ</span>
</button>
</div>
@@ -214,64 +388,136 @@
</div>
<!-- Table View -->
<div v-if="viewMode === 'table'" class="table-container schedule-container">
<div
v-if="viewMode === 'table'"
class="table-container schedule-container"
>
<table class="table is-fullwidth is-hoverable is-size-6">
<thead>
<tr>
<th class="has-background-primary has-text-white has-font-weight-normal" style="border: none;">Đợt
thanh toán</th>
<th class="has-background-primary has-text-white has-font-weight-normal has-text-right"
style="border: none;">Số tiền (VND)</th>
<th class="has-background-primary has-text-white has-font-weight-normal has-text-right"
style="border: none;">Đã thanh toán</th>
<th class="has-background-primary has-text-white has-font-weight-normal has-text-right"
style="border: none;">Còn phải TT</th>
<th class="has-background-primary has-text-white has-font-weight-normal" style="border: none;">Ngày
bắt đầu</th>
<th class="has-background-primary has-text-white has-font-weight-normal" style="border: none;">Ngày
đến hạn</th>
<th class="has-background-primary has-text-white has-font-weight-normal" style="border: none;">Trạng
thái</th>
<th
class="has-background-primary has-text-white has-font-weight-normal"
style="border: none"
>
Đợt thanh toán
</th>
<th
class="has-background-primary has-text-white has-font-weight-normal has-text-right"
style="border: none"
>
Số tiền (VND)
</th>
<th
class="has-background-primary has-text-white has-font-weight-normal has-text-right"
style="border: none"
>
Đã thanh toán
</th>
<th
class="has-background-primary has-text-white has-font-weight-normal has-text-right"
style="border: none"
>
Còn phải TT
</th>
<th
class="has-background-primary has-text-white has-font-weight-normal"
style="border: none"
>
Ngày bắt đầu
</th>
<th
class="has-background-primary has-text-white has-font-weight-normal"
style="border: none"
>
Ngày đến hạn
</th>
<th
class="has-background-primary has-text-white has-font-weight-normal"
style="border: none"
>
Trạng thái
</th>
</tr>
</thead>
<tbody>
<tr v-for="(plan, index) in displaySchedule" :key="`plan-${index}`"
style="border-bottom: 1px solid #f5f5f5;"
:class="plan.is_merged ? 'has-background-warning-light' : ''">
<td class="has-text-weight-semibold" :class="plan.is_merged ? 'has-text-warning' : ''">
<tr
v-for="(plan, index) in displaySchedule"
:key="`plan-${index}`"
style="border-bottom: 1px solid #f5f5f5"
:class="plan.is_merged ? 'has-background-warning-light' : ''"
>
<td
class="has-text-weight-semibold"
:class="plan.is_merged ? 'has-text-warning' : ''"
>
Đợt {{ plan.cycle }}
<span v-if="plan.is_merged" class="tag is-warning is-light ml-1 is-size-7">GỘP SỚM</span>
<span
v-if="plan.is_merged"
class="tag is-warning is-light ml-1 is-size-7"
>GỘP SỚM</span
>
</td>
<td class="has-text-right">
<div v-if="plan.is_merged" class="has-text-right">
<p class="has-text-grey" title="Tổng các đợt gốc">{{ $numtoString(totalOriginalEarlyAmount) }}
<div
v-if="plan.is_merged"
class="has-text-right"
>
<p
class="has-text-grey"
title="Tổng các đợt gốc"
>
{{ $numtoString(totalOriginalEarlyAmount) }}
</p>
<p
class="has-text-danger"
title="Chiết khấu thanh toán sớm"
>
- {{ $numtoString(totalEarlyDiscount) }}
</p>
<hr
class="my-1"
style="background: hsla(0, 0%, 0%, 0.2); margin-left: auto; width: 50%"
/>
<p class="has-text-weight-bold">
{{ $numtoString(plan.amount) }}
</p>
<p class="has-text-danger" title="Chiết khấu thanh toán sớm">- {{
$numtoString(totalEarlyDiscount) }}</p>
<hr class="my-1"
style="background: hsla(0, 0%, 0%, 0.2); margin-left: auto; width: 50%;">
<p class="has-text-weight-bold">{{ $numtoString(plan.amount) }}</p>
</div>
<span v-else>{{ $numtoString(plan.amount) }}</span>
</td>
<td class="has-text-right has-text-success">{{ $numtoString(plan.paid_amount) }}</td>
<td class="has-text-right has-text-danger">{{ $numtoString(plan.remain_amount) }}</td>
<td class="has-text-right has-text-success">
{{ $numtoString(plan.paid_amount) }}
</td>
<td class="has-text-right has-text-danger">
{{ $numtoString(plan.remain_amount) }}
</td>
<td>{{ formatDate(plan.from_date) }}</td>
<td>{{ formatDate(plan.to_date) }}</td>
<td>
<span v-if="plan.status === 2" class="tag is-success">Đã thanh toán</span>
<span v-else class="tag is-warning">Chờ thanh toán</span>
<span
v-if="plan.status === 2"
class="tag is-success"
>Đã thanh toán</span
>
<span
v-else
class="tag is-warning"
>Chờ thanh toán</span
>
</td>
</tr>
</tbody>
<tfoot>
<tr class="has-background-light">
<th class="has-text-right has-text-weight-bold">Tổng cộng</th>
<th class="has-text-right has-text-weight-bold">{{ $numtoString(totalAmount) }}</th>
<th class="has-text-right has-text-weight-bold has-text-success">{{ $numtoString(totalPaid) }}
<th class="has-text-right has-text-weight-bold">
{{ $numtoString(totalAmount) }}
</th>
<th class="has-text-right has-text-weight-bold has-text-success">
{{ $numtoString(totalPaid) }}
</th>
<th class="has-text-right has-text-weight-bold has-text-danger">
{{ $numtoString(calculatorData.totalRemaining) }}
</th>
<th class="has-text-right has-text-weight-bold has-text-danger">{{
$numtoString(calculatorData.totalRemaining) }}</th>
<th colspan="3"></th>
</tr>
</tfoot>
@@ -279,28 +525,57 @@
</div>
<!-- List View (Card) -->
<div v-else-if="viewMode === 'list'" class="schedule-container">
<div v-for="(plan, index) in displaySchedule" :key="`card-${index}`" class="card mb-4"
:class="plan.is_merged ? 'has-background-warning-light' : ''">
<div
v-else-if="viewMode === 'list'"
class="schedule-container"
>
<div
v-for="(plan, index) in displaySchedule"
:key="`card-${index}`"
class="card mb-4"
:class="plan.is_merged ? 'has-background-warning-light' : ''"
>
<div class="card-content">
<div class="level is-mobile mb-5">
<div class="level-left">
<div class="level-item">
<span class="tag is-primary" :class="plan.is_merged ? 'is-warning' : ''">Đợt {{ plan.cycle
}}</span>
<span v-if="plan.is_merged" class="tag is-warning is-light ml-1 is-size-7">GỘP SỚM</span>
<span
class="tag is-primary"
:class="plan.is_merged ? 'is-warning' : ''"
>Đợt {{ plan.cycle }}</span
>
<span
v-if="plan.is_merged"
class="tag is-warning is-light ml-1 is-size-7"
>GỘP SỚM</span
>
</div>
</div>
<div class="level-right">
<div class="level-item has-text-weight-bold">
<div v-if="plan.is_merged" class="has-text-right">
<p class="has-text-grey" title="Tổng các đợt gốc">{{ $numtoString(totalOriginalEarlyAmount)
}}</p>
<p class="has-text-danger" title="Chiết khấu thanh toán sớm">- {{
$numtoString(totalEarlyDiscount) }}</p>
<hr class="my-1"
style="background: hsla(0, 0%, 0%, 0.2); margin-left: auto">
<p class="has-text-weight-bold">{{ $numtoString(plan.amount) }}</p>
<div
v-if="plan.is_merged"
class="has-text-right"
>
<p
class="has-text-grey"
title="Tổng các đợt gốc"
>
{{ $numtoString(totalOriginalEarlyAmount) }}
</p>
<p
class="has-text-danger"
title="Chiết khấu thanh toán sớm"
>
- {{ $numtoString(totalEarlyDiscount) }}
</p>
<hr
class="my-1"
style="background: hsla(0, 0%, 0%, 0.2); margin-left: auto"
/>
<p class="has-text-weight-bold">
{{ $numtoString(plan.amount) }}
</p>
</div>
<span v-else>{{ $numtoString(plan.amount) }}</span>
</div>
@@ -308,25 +583,41 @@
</div>
<div class="level is-mobile mb-1">
<div class="level-left">Đã thanh toán:</div>
<div class="level-right has-text-success">{{ $numtoString(plan.paid_amount) }}</div>
<div class="level-right has-text-success">
{{ $numtoString(plan.paid_amount) }}
</div>
</div>
<div class="level is-mobile mb-1">
<div class="level-left">Còn phải TT:</div>
<div class="level-right has-text-danger">{{ $numtoString(plan.remain_amount) }}</div>
<div class="level-right has-text-danger">
{{ $numtoString(plan.remain_amount) }}
</div>
</div>
<div class="level is-mobile mb-1">
<div class="level-left">Từ ngày:</div>
<div class="level-right">{{ formatDate(plan.from_date) }}</div>
<div class="level-right">
{{ formatDate(plan.from_date) }}
</div>
</div>
<div class="level is-mobile mb-1">
<div class="level-left">Đến hạn:</div>
<div class="level-right">{{ formatDate(plan.to_date) }}</div>
<div class="level-right">
{{ formatDate(plan.to_date) }}
</div>
</div>
<div class="level is-mobile">
<div class="level-left">Trạng thái:</div>
<div class="level-right">
<span v-if="plan.status === 2" class="tag is-success">Đã thanh toán</span>
<span v-else class="tag is-warning">Chờ thanh toán</span>
<span
v-if="plan.status === 2"
class="tag is-success"
>Đã thanh toán</span
>
<span
v-else
class="tag is-warning"
>Chờ thanh toán</span
>
</div>
</div>
</div>
@@ -335,7 +626,10 @@
</div>
<!-- Summary Footer -->
<div class="" style="border-top: 1px solid #eee;">
<div
class=""
style="border-top: 1px solid #eee"
>
<div class="level is-mobile is-size-6 my-4">
<div class="level-right">
<div class="level-item">
@@ -354,22 +648,22 @@
</template>
<script setup>
import { ref, computed } from 'vue';
import dayjs from 'dayjs';
import { ref, computed } from "vue";
import dayjs from "dayjs";
// Props - CHỈ NHẬN DỮ LIỆU ĐÃ TÍNH TOÁN
const props = defineProps({
productData: {
type: Object,
default: null
default: null,
},
selectedPolicy: {
type: Object,
default: null
default: null,
},
selectedCustomer: {
type: Object,
default: null
default: null,
},
calculatorData: {
type: Object,
@@ -390,15 +684,15 @@ const props = defineProps({
},
isLoading: {
type: Boolean,
default: false
}
default: false,
},
});
// Emits
const emit = defineEmits(['print']);
const emit = defineEmits(["print"]);
// Local state
const viewMode = ref('table');
const viewMode = ref("table");
// Computed - CHỈ HIỂN THỊ, KHÔNG TÍNH TOÁN
const displaySchedule = computed(() => {
@@ -426,8 +720,8 @@ const totalPaid = computed(() => {
});
const formatDate = (date) => {
if (!date) return '-';
return dayjs(date).format('DD/MM/YYYY');
if (!date) return "-";
return dayjs(date).format("DD/MM/YYYY");
};
</script>
@@ -501,4 +795,4 @@ th,
page-break-inside: avoid !important;
}
}
</style>
</style>

File diff suppressed because it is too large Load Diff