-
-
![]()
+ style="
+ flex: 1;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 16px;
+ background: rgba(255, 255, 255, 0.1);
+ min-height: 140px;
+ "
+ >
+
+
-
-
-
+
+
{{ file.file__name }}
-
{{ $formatFileSize(file.file__size) }}
+
+ {{ $formatFileSize(file.file__size) }}
+
-
@@ -158,7 +256,10 @@
-
@@ -166,11 +267,19 @@
-
+
Chưa có loại tài liệu được định nghĩa cho giai đoạn này.
-
+
@@ -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);
}
},
- }
+ },
};
@@ -420,4 +553,4 @@ export default {
transform: rotate(360deg);
}
}
-
\ No newline at end of file
+
diff --git a/app/components/application/NoPermission.vue b/app/components/application/NoPermission.vue
index 4e31727..13a00c3 100644
--- a/app/components/application/NoPermission.vue
+++ b/app/components/application/NoPermission.vue
@@ -1,4 +1,4 @@
Rất tiếc, bạn hiện chưa có quyền xem thông tin sản phẩm này.
-
\ No newline at end of file
+
diff --git a/app/components/application/PaymentSchedule.vue b/app/components/application/PaymentSchedule.vue
index 1892430..6dd7d83 100644
--- a/app/components/application/PaymentSchedule.vue
+++ b/app/components/application/PaymentSchedule.vue
@@ -1,22 +1,43 @@
-
+
-
+
-
+
Không có chính sách thanh toán.
-
+
{{ selectedPolicy.name }}
@@ -25,82 +46,152 @@
Đơn vị: VNĐ
-
+
In
-
+
Sản phẩm
-
{{ productData.trade_code || productData.code }}
+
+ {{ productData.trade_code || productData.code }}
+
Giá niêm yết
{{ $numtoString(originPrice) }}
-
+
Tổng chiết khấu
-
{{ $numtoString(discountValueDisplay) }}
+
+ {{ $numtoString(discountValueDisplay) }}
+
Giá sau chiết khấu
-
{{ $numtoString(priceAfterDiscount) }}
+
+ {{ $numtoString(priceAfterDiscount) }}
+
-
+
Giá trị bảo đảm
-
{{ $numtoString(allocatedPrice) }}
+
+ {{ $numtoString(allocatedPrice) }}
+
Đặt cọc
-
{{ $numtoString(selectedPolicy.deposit) }}
+
+ {{ $numtoString(selectedPolicy.deposit) }}
+
Khách hàng
-
{{ selectedCustomer.code }} -
- {{ selectedCustomer.fullname }}
-
Chưa chọn
+
+ {{ selectedCustomer.code }} - {{ selectedCustomer.fullname }}
+
+
+ Chưa chọn
+
-
+
-
-
- CHI TIẾT CHIẾT KHẤU:
-
+
+
CHI TIẾT CHIẾT KHẤU:
- | Diễn giải chiết khấu |
- Giá trị |
- Thành tiền |
- Còn lại |
+
+ Diễn giải chiết khấu
+ |
+
+ Giá trị
+ |
+
+ Thành tiền
+ |
+
+ Còn lại
+ |
- | Giá gốc |
- {{ $numtoString(originPrice) }} |
+
+ Giá gốc
+ |
+
+ {{ $numtoString(originPrice) }}
+ |
-
- | {{ idx + 1 }} |
- {{ item.name }} {{ item.code }} |
- {{ item.customType === 1 ? item.customValue + '%' :
- $numtoString(item.customValue) }} |
+
+ |
+ {{ idx + 1 }}
+ |
+
+ {{ item.name }}
+ {{ item.code }}
+ |
+
+ {{ item.customType === 1 ? item.customValue + "%" : $numtoString(item.customValue) }}
+ |
-{{ $numtoString(item.amount) }} |
- {{ $numtoString(item.remaining) }} |
+
+ {{ $numtoString(item.remaining) }}
+ |
-
+
BẢNG DÙNG TIỀN THEO CHÍNH SÁCH BÁN HÀNG
@@ -118,15 +209,22 @@
| 0 |
- {{ $numtoString(selectedPolicy.deposit) }} |
+
+ {{ $numtoString(selectedPolicy.deposit) }}
+ |
- |
- |
- |
Tiền đặt cọc |
-
+
| {{ plan.cycle }} |
- {{ $numtoString(plan.originalCalculatedAmount) }} |
+
+ {{ $numtoString(plan.originalCalculatedAmount) }}
+ |
{{ plan.dueDate }} |
{{ plan.days }} |
{{ plan.displayValue }} |
@@ -135,38 +233,74 @@
- | Tổng cộng |
- {{ $numtoString(priceAfterDiscount) }} |
+
+ Tổng cộng
+ |
+
+ {{ $numtoString(priceAfterDiscount) }}
+ |
|
-
+
-
+
BẢNG DÙNG TIỀN THEO CHƯƠNG TRÌNH THANH TOÁN SỚM BẰNG VỐN TỰ CÓ
@@ -186,7 +320,9 @@
| 0 |
- {{ $numtoString(selectedPolicy.deposit) }} |
+
+ {{ $numtoString(selectedPolicy.deposit) }}
+ |
- |
- |
- |
@@ -194,9 +330,14 @@
0 |
Tiền đặt cọc |
-
+
| {{ plan.cycle }} |
- {{ $numtoString(plan.originalCalculatedAmount) }} |
+
+ {{ $numtoString(plan.originalCalculatedAmount) }}
+ |
{{ plan.dueDate }} |
{{ plan.actualDueDate }}
@@ -210,14 +351,23 @@
0.019%
-
|
- {{ $numtoString(plan.discountAmount) }} |
+
+ {{ $numtoString(plan.discountAmount) }}
+ |
{{ plan.payment_note }} |
- | Tổng cộng |
- {{ $numtoString(totalEarlyDiscount) }} |
+
+ Tổng cộng
+ |
+
+ {{ $numtoString(totalEarlyDiscount) }}
+ |
|
@@ -225,20 +375,29 @@
-
+
Tổng cộng:
- {{ $numtoString(allocatedPrice) }}
+ {{ $numtoString(allocatedPrice) }}
-
- - {{ $numtoString(totalEarlyDiscount) }}
+
+ - {{ $numtoString(totalEarlyDiscount) }}
-
- = {{ $numtoString(totalRealPayment) }}
+
+ = {{ $numtoString(totalRealPayment) }}
@@ -249,75 +408,152 @@
-
- LỊCH THANH TOÁN
-
-
+ LỊCH THANH TOÁN
+
Từ ngày: {{ formatDate(baseDate) }}
-
-
+
- | Đợt thanh toán |
- Diễn giải |
- Tỷ lệ |
- Số tiền |
- Thời gian |
- Hạn thanh toán |
+
+ Đợt thanh toán
+ |
+
+ Diễn giải
+ |
+
+ Tỷ lệ
+ |
+
+ Số tiền
+ |
+
+ Thời gian
+ |
+
+ Hạn thanh toán
+ |
-
-
-
- Đợt {{ plan.displayCycle }}
-
-
+
+ |
+ Đợt {{ plan.displayCycle }}
+
GỘP SỚM
|
-
-
+
+
- Thanh toán sớm gộp
- (Đợt {{ plan.mergedCycles.join(', ') }})
+ Thanh toán sớm gộp (Đợt
+ {{ plan.mergedCycles.join(", ") }})
+
+
+ {{ plan.payment_note || "-" }}
- {{ plan.payment_note || '-' }}
- {{ plan.payment_note || '-' }}
- {{ plan.due_note }}
+ {{ plan.payment_note || "-" }}
+
+ {{ plan.due_note }}
+
|
-
+ |
{{ plan.mergedCyclesRates }}
{{ plan.displayValue }}
|
-
-
+
+
Tổng các đợt: {{ $numtoString(plan.mergedRawAmount) }}
- - Số tiền chiết khấu sớm: {{ $numtoString(totalEarlyDiscount) }}
+ - Số tiền chiết khấu sớm:
+ {{ $numtoString(totalEarlyDiscount) }}
-
+
- Đặt cọc: {{ $numtoString(selectedPolicy.deposit) }}
@@ -328,21 +564,37 @@
{{ $numtoString(plan.amountBeforeDeposit) }}
-
- - {{ $numtoString(selectedPolicy.deposit) }}
-
+ - {{ $numtoString(selectedPolicy.deposit) }}
Còn lại: {{ $numtoString(plan.calculatedAmount) }}
- {{ $numtoString(plan.calculatedAmount) }}
+ {{ $numtoString(plan.calculatedAmount) }}
|
-
- {{ plan.days }} ngày
+ |
+ {{ plan.days }} ngày
-
|
-
- {{ plan.dueDate }}
+ |
+ {{ plan.dueDate }}
-
|
| |
@@ -350,48 +602,87 @@
|
-
-
+
+
{{ plan.displayCycle }}
-
- Đợt {{ plan.displayCycle }}
-
-
+ Đợt {{ plan.displayCycle }}
+
GỘP SỚM
-
-
{{ $numtoString(plan.calculatedAmount) }}
+
+
+ {{ $numtoString(plan.calculatedAmount) }}
+
Với chiết khấu sớm
-
+
- {{ $numtoString(plan.amountBeforeDeposit) }} - {{ $numtoString(selectedPolicy.deposit) }}
+ {{ $numtoString(plan.amountBeforeDeposit) }} -
+ {{ $numtoString(selectedPolicy.deposit) }}
-
{{ $numtoString(plan.calculatedAmount) }}
+
{{
+ $numtoString(plan.calculatedAmount)
+ }}
-
{{ $numtoString(plan.calculatedAmount) }}
+
{{ $numtoString(plan.calculatedAmount) }}
-
-
- Thanh toán sớm gộp
- (Đợt {{ plan.mergedCycles.join(', ') }})
+
+
Thanh toán sớm gộp (Đợt {{ plan.mergedCycles.join(", ") }})
+
+ {{ plan.mergedCyclesRates }}
-
{{ plan.mergedCyclesRates }}
-
-
Diễn giải: {{ plan.payment_note }}
+
+ Diễn giải:
+ {{ plan.payment_note }}
+
+
+ {{ plan.due_note }}
-
{{ plan.due_note }}
-
+
Hạn: {{ plan.dueDate }} - {{ plan.days }}ngày
-
@@ -404,12 +695,23 @@
-
-
+
+
Đang tải kế hoạch...
-
Chưa có dữ liệu kế hoạch
+
+ Chưa có dữ liệu kế hoạch
+
@@ -419,13 +721,13 @@
@@ -878,4 +1182,4 @@ th {
page-break-inside: avoid !important;
}
}
-
\ No newline at end of file
+
diff --git a/app/components/application/PaymentSchedulePresentation.vue b/app/components/application/PaymentSchedulePresentation.vue
index 1b8c163..bf046a3 100644
--- a/app/components/application/PaymentSchedulePresentation.vue
+++ b/app/components/application/PaymentSchedulePresentation.vue
@@ -1,8 +1,15 @@
-
+
-
+
@@ -12,101 +19,186 @@
Đơn vị: VNĐ
-
+
In
-
+
Giá niêm yết
-
{{ $numtoString(calculatorData.originPrice) }}
+
+ {{ $numtoString(calculatorData.originPrice) }}
+
Tổng chiết khấu
-
{{ $numtoString(calculatorData.totalDiscount) }}
+
+ {{ $numtoString(calculatorData.totalDiscount) }}
+
Giá sau chiết khấu
-
{{ $numtoString(calculatorData.salePrice) }}
+
+ {{ $numtoString(calculatorData.salePrice) }}
+
-
+
Giá trị bảo đảm
-
{{ $numtoString(calculatorData.allocatedPrice) }}
+
+ {{ $numtoString(calculatorData.allocatedPrice) }}
+
-
+
Đặt cọc
-
{{ $numtoString(selectedPolicy.deposit) }}
+
+ {{ $numtoString(selectedPolicy.deposit) }}
+
Khách hàng
-
+
{{ selectedCustomer.code }} - {{ selectedCustomer.fullname }}
-
Chưa chọn
+
+ Chưa chọn
+
-
+
-
-
- CHI TIẾT CHIẾT KHẤU:
-
+
+
CHI TIẾT CHIẾT KHẤU:
- | Diễn giải chiết khấu |
- Giá trị |
- Thành tiền |
- Còn lại |
+
+ Diễn giải chiết khấu
+ |
+
+ Giá trị
+ |
+
+ Thành tiền
+ |
+
+ Còn lại
+ |
-
- | Giá gốc |
- {{
- $numtoString(calculatorData.originPrice) }} |
+
+ |
+ Giá gốc
+ |
+
+ {{ $numtoString(calculatorData.originPrice) }}
+ |
-
- | {{ idx + 1 }} |
+
+ |
+ {{ idx + 1 }}
+ |
{{ item.name }}
{{ item.code }}
|
- {{ item.customType === 1 ? item.customValue + '%' :
- $numtoString(item.customValue) }} |
+
+ {{ item.customType === 1 ? item.customValue + "%" : $numtoString(item.customValue) }}
+ |
-{{ $numtoString(item.amount) }} |
- {{ $numtoString(item.remaining) }} |
+
+ {{ $numtoString(item.remaining) }}
+ |
-
+
LỊCH THANH TOÁN GỐC (THEO CHÍNH SÁCH)
@@ -115,26 +207,57 @@
- | Đợt
+ |
+ Đợt
+ |
+
+ Tỷ lệ
+ |
+
+ Số tiền (VND)
+ |
+
+ Ngày bắt đầu
+ |
+
+ Ngày đến hạn
+ |
+
+ Số ngày
|
- Tỷ lệ |
- Số tiền (VND) |
- Ngày
- bắt đầu |
- Ngày
- đến hạn |
- Số ngày |
-
+
| Đợt {{ plan.cycle }} |
- {{ plan.type === 1 ? `${plan.value}%` : '-' }} |
- {{ $numtoString(plan.amount) }} |
+
+ {{ plan.type === 1 ? `${plan.value}%` : "-" }}
+ |
+
+ {{ $numtoString(plan.amount) }}
+ |
{{ formatDate(plan.from_date) }} |
{{ formatDate(plan.to_date) }} |
{{ plan.days }} |
@@ -151,29 +274,62 @@
- | Đợt
+ |
+ Đợt
+ |
+
+ Hạn TT Gốc
+ |
+
+ Ngày TT Thực Tế
+ |
+
+ Số tiền gốc
+ |
+
+ Số ngày TT sớm
+ |
+
+ Tỷ lệ CK (%/ngày)
+ |
+
+ Tiền chiết khấu
|
- Hạn
- TT Gốc |
- Ngày
- TT Thực Tế |
- Số tiền gốc |
- Số ngày TT sớm |
- Tỷ lệ CK (%/ngày) |
- Tiền chiết khấu |
-
+
| Đợt {{ item.cycle }} |
{{ formatDate(item.original_payment_date) }} |
{{ formatDate(item.actual_payment_date) }} |
- {{ $numtoString(item.original_amount) }} |
+
+ {{ $numtoString(item.original_amount) }}
+ |
{{ item.early_days }} |
{{ item.discount_rate }} |
-{{ $numtoString(item.discount_amount) }} |
@@ -181,9 +337,15 @@
- | Tổng chiết khấu thanh toán sớm |
- -{{
- $numtoString(totalEarlyDiscount) }} |
+
+ Tổng chiết khấu thanh toán sớm
+ |
+
+ -{{ $numtoString(totalEarlyDiscount) }}
+ |
@@ -191,7 +353,10 @@
-
+
@@ -199,14 +364,23 @@
LỊCH THANH TOÁN
-
+
-
+
Bảng
-
+
Thẻ
@@ -214,64 +388,136 @@
-
+
- | Đợt
- thanh toán |
- Số tiền (VND) |
- Đã thanh toán |
- Còn phải TT |
- Ngày
- bắt đầu |
- Ngày
- đến hạn |
- Trạng
- thái |
+
+ Đợt thanh toán
+ |
+
+ Số tiền (VND)
+ |
+
+ Đã thanh toán
+ |
+
+ Còn phải TT
+ |
+
+ Ngày bắt đầu
+ |
+
+ Ngày đến hạn
+ |
+
+ Trạng thái
+ |
-
- |
+ |
+ |
Đợt {{ plan.cycle }}
- GỘP SỚM
+ GỘP SỚM
|
-
- {{ $numtoString(totalOriginalEarlyAmount) }}
+
+
+ {{ $numtoString(totalOriginalEarlyAmount) }}
+
+
+ - {{ $numtoString(totalEarlyDiscount) }}
+
+
+
+ {{ $numtoString(plan.amount) }}
- - {{
- $numtoString(totalEarlyDiscount) }}
-
- {{ $numtoString(plan.amount) }}
{{ $numtoString(plan.amount) }}
|
- {{ $numtoString(plan.paid_amount) }} |
- {{ $numtoString(plan.remain_amount) }} |
+
+ {{ $numtoString(plan.paid_amount) }}
+ |
+
+ {{ $numtoString(plan.remain_amount) }}
+ |
{{ formatDate(plan.from_date) }} |
{{ formatDate(plan.to_date) }} |
- Đã thanh toán
- Chờ thanh toán
+ Đã thanh toán
+ Chờ thanh toán
|
| Tổng cộng |
- {{ $numtoString(totalAmount) }} |
- {{ $numtoString(totalPaid) }}
+ |
+ {{ $numtoString(totalAmount) }}
+ |
+
+ {{ $numtoString(totalPaid) }}
+ |
+
+ {{ $numtoString(calculatorData.totalRemaining) }}
|
- {{
- $numtoString(calculatorData.totalRemaining) }} |
|
@@ -279,28 +525,57 @@
-
-
+
+
- Đợt {{ plan.cycle
- }}
- GỘP SỚM
+ Đợt {{ plan.cycle }}
+ GỘP SỚM
-
-
{{ $numtoString(totalOriginalEarlyAmount)
- }}
-
- {{
- $numtoString(totalEarlyDiscount) }}
-
-
{{ $numtoString(plan.amount) }}
+
+
+ {{ $numtoString(totalOriginalEarlyAmount) }}
+
+
+ - {{ $numtoString(totalEarlyDiscount) }}
+
+
+
+ {{ $numtoString(plan.amount) }}
+
{{ $numtoString(plan.amount) }}
@@ -308,25 +583,41 @@
Đã thanh toán:
-
{{ $numtoString(plan.paid_amount) }}
+
+ {{ $numtoString(plan.paid_amount) }}
+
Còn phải TT:
-
{{ $numtoString(plan.remain_amount) }}
+
+ {{ $numtoString(plan.remain_amount) }}
+
Từ ngày:
-
{{ formatDate(plan.from_date) }}
+
+ {{ formatDate(plan.from_date) }}
+
Đến hạn:
-
{{ formatDate(plan.to_date) }}
+
+ {{ formatDate(plan.to_date) }}
+
Trạng thái:
- Đã thanh toán
- Chờ thanh toán
+ Đã thanh toán
+ Chờ thanh toán
@@ -335,7 +626,10 @@
-
+
@@ -354,22 +648,22 @@
@@ -501,4 +795,4 @@ th,
page-break-inside: avoid !important;
}
}
-
\ No newline at end of file
+
diff --git a/app/components/application/PhaseAdvance.vue b/app/components/application/PhaseAdvance.vue
index 50b2425..0f0dd1b 100644
--- a/app/components/application/PhaseAdvance.vue
+++ b/app/components/application/PhaseAdvance.vue
@@ -3,34 +3,51 @@
-
-
+
+
Xem hợp đồng
-
+
Chuyển tiếp
-
-
+
+
@@ -40,17 +57,25 @@
Mã sản phẩm
-
+
{{ selectedProduct.trade_code }}
-
+
Loại
-
{{ selectedProduct.type__name }}
+
+ {{ selectedProduct.type__name }}
+
Diện tích đất
@@ -58,7 +83,9 @@
Phân khu
-
{{ selectedProduct.zone_type__name }}
+
+ {{ selectedProduct.zone_type__name }}
+
@@ -69,10 +96,10 @@
Thông tin Khách hàng
-
Đổi khách hàng
@@ -83,31 +110,58 @@
Mã khách hàng
-
{{ transactionData.customer__code }}
+
+ {{ transactionData.customer__code }}
+
Số điện thoại
-
{{ transactionData.customer__phone }}
+
+ {{ transactionData.customer__phone }}
+
Họ và tên
-
{{ transactionData.customer__fullname }}
+
+ {{ transactionData.customer__fullname }}
+
-
{{ transactionData.customer__legal_type__name }}
-
{{ transactionData.customer__legal_code }}
+
+ {{ transactionData.customer__legal_type__name }}
+
+
+ {{ transactionData.customer__legal_code }}
+
-
+
Người đồng sở hữu
Họ và tên
-
{{ coOwner.people__fullname }}
+
+ {{ coOwner.people__fullname }}
+
Số điện thoại
-
{{ coOwner.people__phone }}
+
+ {{ coOwner.people__phone }}
+
@@ -115,55 +169,89 @@
Chọn khách hàng mới để thay thế
-
-
-
+
+
+
Quản lý đồng sở hữu
-
-
-
-
-
- {{ pendingCoOwner?.action === 'add' ? pendingCoOwner.data.people__fullname : coOwner.people__fullname }}
-
-
-
-
-
+
+
+
+
+
+ {{
+ pendingCoOwner?.action === "add"
+ ? pendingCoOwner.data.people__fullname
+ : coOwner.people__fullname
+ }}
+
+
+
+
+
-
-
-
-
-
Chỉ khách hàng cá nhân mới có thể thêm đồng sở hữu.
+
+
+
+
+
+ Chỉ khách hàng cá nhân mới có thể thêm đồng sở hữu.
+
- Hủy
- Xác nhận
+
+ Hủy
+
+
+ Xác nhận
+
@@ -179,24 +267,50 @@
-
-
+
+
-
+
Chọn chiết khấu
-
-
+ style="cursor: move; border-bottom: 1px solid #204853"
+ >
-
+
-
-
-
handleRowSelect(index, val)" />
+
+
+ handleRowSelect(index, val)"
+ />
-
-
+
+
-
-
-
-
+
-
+
Thêm chiết khấu
@@ -279,17 +427,33 @@
-
+
-
+
-
@@ -325,26 +501,39 @@
Mã giao dịch
-
+
{{ transactionData.code }}
-
+
Giai đoạn hiện tại
-
{{ transactionData.phase__name }}
+
+ {{ transactionData.phase__name }}
+
Chính sách bán hàng
-
{{ currentPolicyName }}
+
+ {{ currentPolicyName }}
+
Ngày ký văn bản
-
{{ formatDate(transactionData.date) }}
+
+ {{ formatDate(transactionData.date) }}
+
-
+
Giá gốc
@@ -352,13 +541,20 @@
Chiết khấu
-
- -{{ $numtoString(isEditingPolicy ? calculationResult.totalDiscount : transactionData.discount_amount) }} đ
+
+ -{{ $numtoString(isEditingPolicy ? calculationResult.totalDiscount : transactionData.discount_amount) }}
+ đ
Giá hợp đồng
-
{{ $numtoString(isEditingPolicy ? calculationResult.salePrice : transactionData.sale_price) }} đ
+
+ {{ $numtoString(isEditingPolicy ? calculationResult.salePrice : transactionData.sale_price) }}
+ đ
+
Tổng đã nhận
@@ -368,39 +564,72 @@
-
-
+
+
+
+
Vui lòng chọn sản phẩm đang giữ chỗ
Để xem thông tin chi tiết và chuyển giai đoạn
-
+
-
-
+
+
Lỗi: {{ error }}
-
-
-
+
+
+
\ No newline at end of file
+
diff --git a/app/components/cash-book/CashBook.vue b/app/components/cash-book/CashBook.vue
index 1e834de..4ef3d92 100644
--- a/app/components/cash-book/CashBook.vue
+++ b/app/components/cash-book/CashBook.vue
@@ -1,7 +1,3 @@
-
-
-
-
- Cash Book
-
+
Cash Book
diff --git a/app/components/common/ActionInfo.vue b/app/components/common/ActionInfo.vue
index e156bb8..8e162d8 100644
--- a/app/components/common/ActionInfo.vue
+++ b/app/components/common/ActionInfo.vue
@@ -25,12 +25,23 @@
-
+
- Cập nhật
+
+ Cập nhật
+
-
-
+
+
@@ -69,24 +98,47 @@
-
+
-
+
- Cập nhật
+
+ Cập nhật
+
-
+
-
-
+
+
@@ -125,8 +196,17 @@
-
-
{{ v.link }}
+
+
{{ v.link }}
diff --git a/app/components/common/Avatarbox.vue b/app/components/common/Avatarbox.vue
index 4758d9c..84c6988 100644
--- a/app/components/common/Avatarbox.vue
+++ b/app/components/common/Avatarbox.vue
@@ -3,20 +3,26 @@ const props = defineProps({
text: String,
image: String,
type: String,
- size: Number
+ size: Number,
});
-
+
-
-
+
+
- {{text}}
+ {{ text }}
diff --git a/app/components/common/CountWithAdd.vue b/app/components/common/CountWithAdd.vue
index d62f454..4e64f73 100644
--- a/app/components/common/CountWithAdd.vue
+++ b/app/components/common/CountWithAdd.vue
@@ -1,41 +1,42 @@
-
+
{{ count }}
-
- +
-
+ +
\ No newline at end of file
+ },
+ },
+};
+
diff --git a/app/components/common/CountdownTimer.vue b/app/components/common/CountdownTimer.vue
index dd1234d..ab2d041 100644
--- a/app/components/common/CountdownTimer.vue
+++ b/app/components/common/CountdownTimer.vue
@@ -1,135 +1,141 @@
-
- {{ isVietnamese ? 'Hết giờ' : 'Expired' }}
+
+ {{ isVietnamese ? "Hết giờ" : "Expired" }}
-
+
{{ formattedTime }}
\ No newline at end of file
+
diff --git a/app/components/common/Editor.vue b/app/components/common/Editor.vue
index 8a1cb04..cb9ce5b 100644
--- a/app/components/common/Editor.vue
+++ b/app/components/common/Editor.vue
@@ -37,7 +37,7 @@ const toolbarOptions = [
[{ header: [1, 2, 3, 4, 5, 6, false] }],
// ✍️ Định dạng cơ bản
- ['bold', 'italic', 'underline', 'strike'],
+ ["bold", "italic", "underline", "strike"],
// 🎨 Màu chữ & nền
[{ color: [] }, { background: [] }],
@@ -46,14 +46,13 @@ const toolbarOptions = [
[{ align: [] }],
// 📋 Danh sách
- [{ list: 'ordered' }, { list: 'bullet' }],
+ [{ list: "ordered" }, { list: "bullet" }],
// 🔗 Media
- ['link', 'image', 'video'],
-
- ['clean'], // Xóa định dạng
-]
+ ["link", "image", "video"],
+ ["clean"], // Xóa định dạng
+];
var content = props.text;
diff --git a/app/components/common/InputEmail.vue b/app/components/common/InputEmail.vue
index 0b8b78b..caf0e9d 100644
--- a/app/components/common/InputEmail.vue
+++ b/app/components/common/InputEmail.vue
@@ -1,37 +1,43 @@
-
-
-
-
-
-
+
+
+
+
+
+
\ No newline at end of file
+ let check = this.$errEmail(this.value);
+ this.error = check ? true : false;
+ this.$emit("email", this.value);
+ },
+ },
+};
+
diff --git a/app/components/common/InputPhone.vue b/app/components/common/InputPhone.vue
index c3fc125..446e364 100644
--- a/app/components/common/InputPhone.vue
+++ b/app/components/common/InputPhone.vue
@@ -10,7 +10,7 @@
autocomplete="tel"
/>
-
+
diff --git a/app/components/common/Note.vue b/app/components/common/Note.vue
index 1b79fa6..ae43652 100644
--- a/app/components/common/Note.vue
+++ b/app/components/common/Note.vue
@@ -1,27 +1,36 @@
-
- {{ $store.lang==='vi'? 'Lưu lại' : 'Save' }}
+
+ {{ $store.lang === "vi" ? "Lưu lại" : "Save" }}
\ No newline at end of file
+const emit = defineEmits(["close"]);
+const { $store, $getdata, $updateapi, $updatepage } = useNuxtApp();
+const props = defineProps({
+ row: Object,
+ pagename: String,
+});
+var record = await $getdata("application", { id: props.row.id }, undefined, true);
+async function save() {
+ await $updateapi("application", record);
+ record = await $getdata("application", { id: props.row.id }, undefined, true);
+ $updatepage(props.pagename, record);
+ emit("close");
+}
+
diff --git a/app/components/common/NoteInfo.vue b/app/components/common/NoteInfo.vue
index ff4cffb..6c175bb 100644
--- a/app/components/common/NoteInfo.vue
+++ b/app/components/common/NoteInfo.vue
@@ -1,19 +1,25 @@
-
-
- Chưa có ghi chú nào được lưu
-
+
+ Chưa có ghi chú nào được lưu
-
+
-
+
@@ -22,14 +28,20 @@
- {{v.user__fullname}}
- {{ $dayjs(v['create_time']).fromNow(true) }}
-
-
-
+ {{ v.user__fullname }}
+ {{ $dayjs(v["create_time"]).fromNow(true) }}
+
+
+
-
-
+
+
@@ -39,87 +51,126 @@
-
\ No newline at end of file
+ let v = this.obj.v;
+ let i = this.obj.i;
+ let rs = await this.$deleteapi(this.api, v.id);
+ if (rs === "error") return;
+ this.$delete(this.data, i);
+ let rows = this.$copy(this.$store[this.pagename].data);
+ let idx = this.$findIndex(rows, { id: this.row.id });
+ let copy = this.$copy(this.row);
+ copy.count_note -= 1;
+ rows[idx] = copy;
+ this.$store.commit("updateState", {
+ name: this.pagename,
+ key: "update",
+ data: { data: rows },
+ });
+ },
+ },
+};
+
diff --git a/app/components/common/Notebox.vue b/app/components/common/Notebox.vue
index 7ac11fd..b445665 100644
--- a/app/components/common/Notebox.vue
+++ b/app/components/common/Notebox.vue
@@ -1,18 +1,24 @@
- {{ row.count_note || '+' }}
+ >{{ row.count_note || "+" }}
\ No newline at end of file
+ let obj = {
+ component: "common/NoteInfo",
+ title: "Ghi chú",
+ width: "50%",
+ vbind: { row: this.row, api: this.api, pagename: this.pagename },
+ };
+ this.$emit("open", { name: "dataevent", data: { modal: obj } });
+ },
+ },
+};
+
diff --git a/app/components/common/Phone.vue b/app/components/common/Phone.vue
index 2e32b61..3b70db1 100644
--- a/app/components/common/Phone.vue
+++ b/app/components/common/Phone.vue
@@ -1,54 +1,82 @@
\ No newline at end of file
+ window.open(`https://zalo.me/${this.phone}`, "_blank");
+ },
+ },
+};
+
diff --git a/app/components/common/ProductCountbox.vue b/app/components/common/ProductCountbox.vue
index dbf1d03..6f83e02 100644
--- a/app/components/common/ProductCountbox.vue
+++ b/app/components/common/ProductCountbox.vue
@@ -1,25 +1,28 @@
-
- {{ row.count_product }}
+ {{ row.count_product }}
\ No newline at end of file
+ pagename: this.pagename,
+ },
+ };
+ this.$emit("open", { name: "dataevent", data: { modal: obj } });
+ },
+ },
+};
+
diff --git a/app/components/common/ProductInfo.vue b/app/components/common/ProductInfo.vue
index d32b939..41e3afc 100644
--- a/app/components/common/ProductInfo.vue
+++ b/app/components/common/ProductInfo.vue
@@ -1,21 +1,24 @@
-
+
-
\ No newline at end of file
+
diff --git a/app/components/common/QRcode.vue b/app/components/common/QRcode.vue
index 31eadad..9839bfb 100644
--- a/app/components/common/QRcode.vue
+++ b/app/components/common/QRcode.vue
@@ -1,27 +1,40 @@
-
{{row.fullname}}
+
+ {{ row.fullname }}
+
-
+
-
+
Không có dữ liệu để tạo QR Code
-
+
-
-
\ No newline at end of file
+ URL.revokeObjectURL(url);
+ $snackbar(isVietnamese.value ? "Đã tải xuống mã QR!" : "QR Code downloaded!", { type: "is-success" });
+ };
+ image.src = url;
+}
+
diff --git a/app/components/common/ViewList.vue b/app/components/common/ViewList.vue
index bc4d514..a1a6f3a 100644
--- a/app/components/common/ViewList.vue
+++ b/app/components/common/ViewList.vue
@@ -1,10 +1,13 @@
-
+
\ No newline at end of file
+ props: ["vbind"],
+};
+
diff --git a/app/components/customer/Company.vue b/app/components/customer/Company.vue
index 7505bf1..9212c67 100644
--- a/app/components/customer/Company.vue
+++ b/app/components/customer/Company.vue
@@ -1,175 +1,279 @@
-
-
-
-
-
-
-
-
{{ errors.code }}
-
-
+
+
+
+
+
+
+
+
+ {{ errors.code }}
+
+
+
-
-
-
-
-
-
-
{{errors.fullname}}
-
+
+
+
+
+
+
+
+ {{ errors.fullname }}
+
+
+
+
+
+
+
+
+
+
+ {{ errors.shortname }}
+
+
+
+
+
+
+
+
+
+
+ {{ errors.legal_code }}
+
+
+
+
+
+
+
+
+
+
+ {{ errors.phone }}
+ Chi tiết
+
+
+
+
+
+
+
+
+
+
+ {{ errors.email }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
{{errors.shortname}}
-
+
+
+ {{ findLang("save") }}
+
-
-
-
-
-
+
-
{{errors.legal_code}}
-
-
-
-
-
-
-
-
-
{{ errors.phone }}
- Chi tiết
-
-
-
-
-
-
-
-
-
-
{{ errors.email }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ findLang('save') }}
-
-
-
\ No newline at end of file
+ record.value = $resetNull(record.value);
+ if (record.value._country) record.value.country = record.value._country.id;
+ if (!record.value.creator) record.value.creator = store.login.id;
+ record.value.updater = store.login.id;
+ record.update_time = new Date();
+ let rs = record.value.id ? await $updateapi("company", record.value) : await $insertapi("company", record.value);
+ if (rs === "error") return;
+ if (!record.value.id) $snackbar(`Khách hàng đã được khởi tạo với mã
${rs.code}`, "Thành công", "Success");
+ record.value.id = rs.id;
+ let ele = await $getdata("company", { id: rs.id }, null, true);
+ emit("update", ele);
+ emit("modalevent", { name: "dataevent", data: ele });
+}
+initData();
+
diff --git a/app/components/customer/Customer.vue b/app/components/customer/Customer.vue
index 483f021..21ab4bd 100644
--- a/app/components/customer/Customer.vue
+++ b/app/components/customer/Customer.vue
@@ -10,14 +10,21 @@
v-for="(v, i) in tabs"
:key="i"
:class="['is-clickable p-3', i !== 0 && 'mt-2', getStyle(v)]"
- style="width: 130px; border-radius: 4px;"
+ style="width: 130px; border-radius: 4px"
@click="changeTab(v)"
>
{{ isVietnamese ? v.name : v.en }}
-
-
+
+
{{ isVietnamese ? v.name : v.en }}
@@ -54,7 +61,11 @@
-
+
\ No newline at end of file
+
diff --git a/app/components/customer/CustomerTypeSelector.vue b/app/components/customer/CustomerTypeSelector.vue
index 7ab0013..ff51463 100644
--- a/app/components/customer/CustomerTypeSelector.vue
+++ b/app/components/customer/CustomerTypeSelector.vue
@@ -28,9 +28,7 @@
>
-
+
{{ isVietnamese ? "Doanh nghiệp" : "Company" }}
diff --git a/app/components/customer/CustomerView.vue b/app/components/customer/CustomerView.vue
index 05676fe..619be86 100644
--- a/app/components/customer/CustomerView.vue
+++ b/app/components/customer/CustomerView.vue
@@ -1,5 +1,8 @@
-
+
@@ -7,7 +10,11 @@
- {{ record.code }}
+ {{ record.code }}
@@ -23,14 +30,21 @@
- {{ record.phone }}
+ {{ record.phone }}
-
+
{{ record.email || "/" }}
@@ -103,7 +117,11 @@
- {{ record.creator__fullname || "/" }}
+ {{ record.creator__fullname || "/" }}
@@ -122,7 +140,11 @@
- {{ record.updater__fullname || "/" }}
+ {{ record.updater__fullname || "/" }}
@@ -131,7 +153,7 @@
- {{ record.update_time ? $dayjs(record.update_time).format("DD/MM/YYYY HH:mm") : '/' }}
+ {{ record.update_time ? $dayjs(record.update_time).format("DD/MM/YYYY HH:mm") : "/" }}
@@ -143,58 +165,137 @@
-
+
-
{{ relatedPerson.people__code }}
-
-
+ {{ relatedPerson.people__fullname }}
+ >{{ relatedPerson.people__fullname }}
({{ relatedPerson.relation__name }})
{{ relatedPerson.people__phone }}
-
- Chưa có {{ this.isIndividual ? 'người liên quan' : 'người đại diện pháp luật' }}
+
+ Chưa có
+ {{ this.isIndividual ? "người liên quan" : "người đại diện pháp luật" }}
-
-
- 0"
+ class="mt-3"
+ >
+
+
+ }"
+ />
-
-
diff --git a/app/components/dashboard/AvatarBox.vue b/app/components/dashboard/AvatarBox.vue
index cdf517e..e702f3f 100644
--- a/app/components/dashboard/AvatarBox.vue
+++ b/app/components/dashboard/AvatarBox.vue
@@ -1,17 +1,18 @@
-
-
+
+ :style="image && 'border: none'"
+ >
- {{text}}
+ {{ text }}
\ No newline at end of file
+
diff --git a/app/components/dashboard/Driver.vue b/app/components/dashboard/Driver.vue
index ab66fa6..dd174de 100644
--- a/app/components/dashboard/Driver.vue
+++ b/app/components/dashboard/Driver.vue
@@ -1,15 +1,15 @@
- {{ status }}
Đơn: {{ deliveries_completed }}/{{ deliveries }}
-
+ >
diff --git a/app/components/dashboard/OrderStatus.vue b/app/components/dashboard/OrderStatus.vue
index 613c152..9fcd58a 100644
--- a/app/components/dashboard/OrderStatus.vue
+++ b/app/components/dashboard/OrderStatus.vue
@@ -1,40 +1,40 @@
@@ -51,4 +51,4 @@ const statuses = [
-
\ No newline at end of file
+
diff --git a/app/components/dashboard/OrderStatusCard.vue b/app/components/dashboard/OrderStatusCard.vue
index ae43e30..15193b8 100644
--- a/app/components/dashboard/OrderStatusCard.vue
+++ b/app/components/dashboard/OrderStatusCard.vue
@@ -6,18 +6,16 @@ const props = defineProps({
value: Number,
icon: String,
color: String,
-})
+});
-
+
-
-
\ No newline at end of file
+
diff --git a/app/components/dashboard/RevenueChart.vue b/app/components/dashboard/RevenueChart.vue
index facf742..50d816d 100644
--- a/app/components/dashboard/RevenueChart.vue
+++ b/app/components/dashboard/RevenueChart.vue
@@ -2,7 +2,7 @@
const { $shortenCurrency } = useNuxtApp();
const revenueChartOptions = {
chart: {
- type: 'spline'
+ type: "spline",
},
credits: {
enabled: false,
@@ -11,38 +11,38 @@ const revenueChartOptions = {
text: null,
},
xAxis: {
- categories: [
- '10/4', '11/4', '12/4', '13/4', '14/4', '15/4', '16/4', '17/4', '18/4'
- ],
+ categories: ["10/4", "11/4", "12/4", "13/4", "14/4", "15/4", "16/4", "17/4", "18/4"],
accessibility: {
- description: 'Dates'
- }
+ description: "Dates",
+ },
},
yAxis: {
title: {
- text: 'Doanh thu'
+ text: "Doanh thu",
},
labels: {
- format: '{value}'
- }
+ format: "{value}",
+ },
},
tooltip: {
crosshairs: true,
shared: true,
- valueSuffix: ' VNĐ',
+ valueSuffix: " VNĐ",
},
plotOptions: {
spline: {
marker: {
- enabled: false
- }
- }
+ enabled: false,
+ },
+ },
},
- series: [{
- name: 'Doanh thu',
- data: [45000000, 52000000, 48000000, 51000000, 58000000, 61000000, 67500000, 72000000, 69000000],
- showInLegend: false,
- }]
+ series: [
+ {
+ name: "Doanh thu",
+ data: [45000000, 52000000, 48000000, 51000000, 58000000, 61000000, 67500000, 72000000, 69000000],
+ showInLegend: false,
+ },
+ ],
};
@@ -58,4 +58,4 @@ const revenueChartOptions = {
-
\ No newline at end of file
+
diff --git a/app/components/dashboard/TopCustomer.vue b/app/components/dashboard/TopCustomer.vue
index d636151..c546bb5 100644
--- a/app/components/dashboard/TopCustomer.vue
+++ b/app/components/dashboard/TopCustomer.vue
@@ -1,11 +1,11 @@
@@ -20,4 +20,4 @@ const { $shortenCurrency } = useNuxtApp();
{{ $shortenCurrency(paid) }}
-
\ No newline at end of file
+
diff --git a/app/components/dashboard/TopCustomers.vue b/app/components/dashboard/TopCustomers.vue
index 2e379d7..8eb1419 100644
--- a/app/components/dashboard/TopCustomers.vue
+++ b/app/components/dashboard/TopCustomers.vue
@@ -1,35 +1,33 @@
@@ -44,4 +42,4 @@ const customers = [
-
\ No newline at end of file
+
diff --git a/app/components/dashboard/TopProduct.vue b/app/components/dashboard/TopProduct.vue
index 6d5ddce..90cf59f 100644
--- a/app/components/dashboard/TopProduct.vue
+++ b/app/components/dashboard/TopProduct.vue
@@ -17,7 +17,11 @@ const { $shortenCurrency } = useNuxtApp();
{{ $shortenCurrency(revenue) }}
-
@@ -26,4 +30,4 @@ const { $shortenCurrency } = useNuxtApp();
.progress {
--bulma-size-small: 0.5rem;
}
-
\ No newline at end of file
+
diff --git a/app/components/dashboard/TopProducts.vue b/app/components/dashboard/TopProducts.vue
index 6c9dfd4..71d3007 100644
--- a/app/components/dashboard/TopProducts.vue
+++ b/app/components/dashboard/TopProducts.vue
@@ -1,33 +1,33 @@
@@ -35,14 +35,14 @@ const products = [
Top sản phẩm
-
\ No newline at end of file
+
diff --git a/app/components/dashboard/Warning.vue b/app/components/dashboard/Warning.vue
index bce3b24..3738538 100644
--- a/app/components/dashboard/Warning.vue
+++ b/app/components/dashboard/Warning.vue
@@ -3,17 +3,16 @@ const props = defineProps({
name: String,
details: String,
level: Number,
- type: String
-})
+ type: String,
+});
const color = computed(() => {
- if (props.level === 1) return 'yellow';
- if (props.level === 2) return 'orange';
- if (props.level === 3) return 'red';
-})
+ if (props.level === 1) return "yellow";
+ if (props.level === 2) return "orange";
+ if (props.level === 3) return "red";
+});
-
-
\ No newline at end of file
+
diff --git a/app/components/dashboard/Warnings.vue b/app/components/dashboard/Warnings.vue
index c1373e1..dec0738 100644
--- a/app/components/dashboard/Warnings.vue
+++ b/app/components/dashboard/Warnings.vue
@@ -1,26 +1,26 @@
@@ -35,4 +35,4 @@ const warnings = [
-
\ No newline at end of file
+
diff --git a/app/components/datatable/ContextMenu.vue b/app/components/datatable/ContextMenu.vue
index 20d2fda..afba02c 100644
--- a/app/components/datatable/ContextMenu.vue
+++ b/app/components/datatable/ContextMenu.vue
@@ -5,7 +5,11 @@
@click="checkFilter() ? false : $emit('modalevent', { name: 'dosort', data: 'az' })"
>
-
+
-
+
-
+
-
+
-
+
-
+
@@ -109,7 +131,10 @@
>
-
+
-
+
-
+
-
+
y === x.code)
+ : !['filter', 'formula'].find((y) => y === x.code),
)"
:key="i"
:class="selectTab.code === v.code ? 'is-active' : 'has-text-primary'"
@@ -193,35 +227,44 @@
/>
-
+
-
+
{{ errors.find((v) => v.name === "label").msg }}
-
+
-
-
-
+
+
+
{{ v.name }}
-
+
-
+
-
+
{{ errors.find((v) => v.name === "unit").msg }}
@@ -261,7 +307,10 @@
class="mr-4"
v-for="(v, i) in colorchoice.filter((v) => v.code !== 'condition')"
>
-
+
-
- {{
- `${currentField.template ? "Sửa" : "Tạo"} định dạng`
- }}
+
+
+ {{ `${currentField.template ? "Sửa" : "Tạo"} định dạng` }}
@@ -308,14 +361,7 @@
import { useStore } from "@/stores/index";
import ScrollBox from "~/components/datatable/ScrollBox";
const store = useStore();
-const {
- $copy,
- $stripHtml,
- $clone,
- $arrayMove,
- $snackbar,
- $copyToClipboard,
-} = useNuxtApp();
+const { $copy, $stripHtml, $clone, $arrayMove, $snackbar, $copyToClipboard } = useNuxtApp();
var props = defineProps({
pagename: String,
field: Object,
@@ -337,9 +383,7 @@ const getMenu = function () {
let field = currentField;
field.disable = "display,tooltip";
let arr = field.disable ? field.disable.split(",") : undefined;
- let array = arr
- ? store.menuchoice.filter((v) => arr.findIndex((x) => x === v.code) < 0)
- : store.menuchoice;
+ let array = arr ? store.menuchoice.filter((v) => arr.findIndex((x) => x === v.code) < 0) : store.menuchoice;
//if (store.login ? !(store.login.is_admin === false) : true) array = [array[0]];
return array;
};
@@ -350,10 +394,7 @@ var value1 = undefined;
var value2 = undefined;
var moneyunit = store.moneyunit;
var radioType = store.datatype.find((v) => v.code === currentField.format);
-var selectUnit =
- currentField.format === "number"
- ? moneyunit.find((v) => v.detail === currentField.unit)
- : undefined;
+var selectUnit = currentField.format === "number" ? moneyunit.find((v) => v.detail === currentField.unit) : undefined;
var bgcolor = undefined;
var radioBGcolor = colorchoice.find((v) => v.code === "none");
var color = undefined;
@@ -366,16 +407,12 @@ var radioMaxWidth = colorchoice.find((v) => v.code === "none");
var maxwidth = undefined;
var selectAlign = undefined;
var radioAlign = colorchoice.find((v) => v.code === "none");
-var radioTemplate = ref(
- colorchoice.find((v) => v.code === (currentField.template ? "option" : "none"))["code"]
-);
+var radioTemplate = ref(colorchoice.find((v) => v.code === (currentField.template ? "option" : "none"))["code"]);
var selectPlacement = store.placement.find((v) => v.code === "is-right");
var selectScheme = store.colorscheme.find((v) => v.code === "is-primary");
var radioTooltip = store.colorchoice.find((v) => v.code === "none");
var selectField = undefined;
-var tags = currentField.tags
- ? currentField.tags.map((v) => fields.find((x) => x.name === v))
- : [];
+var tags = currentField.tags ? currentField.tags.map((v) => fields.find((x) => x.name === v)) : [];
var formula = currentField.formula ? currentField.formula : undefined;
var decimal = currentField.decimal;
let shortmenu = store.menuchoice.filter((x) =>
@@ -383,7 +420,7 @@ let shortmenu = store.menuchoice.filter((x) =>
? currentField.formula
? true
: x.code !== "formula"
- : !["filter", "formula"].find((y) => y === x.code)
+ : !["filter", "formula"].find((y) => y === x.code),
);
var selectTab = shortmenu.find((v) => selectTab.code === v.code)
? selectTab
@@ -448,9 +485,7 @@ function tableOption() {
}
const getFields = function () {
fields = pagedata ? $copy(pagedata.fields) : [];
- fields.map(
- (v) => (v.caption = (v.label ? v.label.indexOf("<") >= 0 : false) ? v.name : v.label)
- );
+ fields.map((v) => (v.caption = (v.label ? v.label.indexOf("<") >= 0 : false) ? v.name : v.label));
};
const doSelect = function (evt) {
emit("modalevent", { name: "selected", data: evt[props.field.name] });
@@ -510,15 +545,16 @@ const saveSetting = function () {
const showSidebar = function () {
let event = { name: "template", field: currentField };
let title = "Danh sách cột";
- if (event.name === "bgcolor")
- title = `Đổi màu nền: ${event.field.name} / ${$stripHtml(event.field.label, 30)}`;
- else if (event.name === "color")
- title = `Đổi màu chữ: ${event.field.name} / ${$stripHtml(event.field.label, 30)}`;
- else if (event.name === "template")
- title = `Định dạng nâng cao: ${$stripHtml(event.field.label, 30)}`;
+ if (event.name === "bgcolor") title = `Đổi màu nền: ${event.field.name} / ${$stripHtml(event.field.label, 30)}`;
+ else if (event.name === "color") title = `Đổi màu chữ: ${event.field.name} / ${$stripHtml(event.field.label, 30)}`;
+ else if (event.name === "template") title = `Định dạng nâng cao: ${$stripHtml(event.field.label, 30)}`;
showmodal.value = {
component: "datatable/FormatOption",
- vbind: { event: event, currentField: currentField, pagename: props.pagename },
+ vbind: {
+ event: event,
+ currentField: currentField,
+ pagename: props.pagename,
+ },
width: "850px",
height: "700px",
title: title,
diff --git a/app/components/datatable/CreateTemplate.vue b/app/components/datatable/CreateTemplate.vue
index 4530665..75b1988 100644
--- a/app/components/datatable/CreateTemplate.vue
+++ b/app/components/datatable/CreateTemplate.vue
@@ -1,180 +1,312 @@
-
-
-
-
-
- {{v.name}}
-
-
-
-
-
-
-
- {{v.name}}
-
-
-
-
-
-
-
- {{v.name}}
-
-
-
-
-
-
-
-
+
+
-
- {{v.name}}
+
+ {{ v.name }}
+
+
+
+
+
+
+
+ {{ v.name }}
+
+
+
+
+
+
+
+ {{ v.name }}
-
-
-
-
{{v.name}}
+
+
+
+
+
+
+ {{ v.name }}
+
+
+
+
-
+
+
+
-
-
-
-
-
+
-
-
-
- {{v.name}}
-
-
-
-
-
-
-
-
-
- {{props.option.name}}
- {{$stripHtml(props.option.label, 50)}}
-
-
- Không có trường thỏa mãn
-
-
+
+
+
+ {{ v.name }}
+
- {{errors.find(v=>v.name==='tagsField').message}}
-
-
-
Click đúp vào để thêm vào biểu thức.
+
+
+
+
+
+
+
+ {{ props.option.name }}
+
+ {{ $stripHtml(props.option.label, 50) }}
+
+
+ Không có trường thỏa mãn
+
+
+
+ {{ errors.find((v) => v.name === "tagsField").message }}
+
+
+
+
Click đúp vào để thêm vào biểu thức.
-
+
-
-
-
{{errors.find(v=>v.name==='expression').message}}
+
+
+
+ {{ errors.find((v) => v.name === "expression").message }}
+
-
-
-
-
-
+
+
+
+
+
-
- {{v.name}}
-
+
+ {{ v.name }}
+
-
-
+
-
- {{v.name}}
+
+ {{ v.name }}
-
@@ -184,119 +316,189 @@
-
- {{v.name}}
-
+
+ {{ v.name }}
+
-
-
-
-
-
-
-
+
+
+
+
+
+
Copy
-
+
-
+
Paste
-
-
-
-
+
+
+
+
Replace
-
+
- Áp dụng
+
+ Áp dụng
+
-
-
+
+
\ No newline at end of file
+ } catch (err) {
+ errors.push({ name: "expression", message: "Biểu thức không hợp lệ" });
+ }
+ returnerrors.length > 0 ? false : true;
+};
+const changeType = function (v) {};
+const doSelect = function (v) {
+ tags.push({ id: $id(), name: v.name, class: getClass(v) });
+ tab = tabs.find((v) => v.code === "selected");
+ selected = tags[tags.length - 1];
+};
+const doSelectSpan = function (v) {
+ tags.push({ id: $id(), name: v.name, class: getSpanClass(v) });
+ tab = tabs.find((v) => v.code === "selected");
+ selected = tags[tags.length - 1];
+};
+const remove = function (i) {
+ $remove(tags, i);
+};
+const getClass = function (v) {
+ let value = type.code + " " + v.code + " " + size.code + (shape.code === "default" ? "" : " " + shape.code);
+ value += outline.code === "default" ? "" : " " + outline.code;
+ return value;
+};
+const getSpanClass = function (v) {
+ let value = "has-text-" + v.name.toLowerCase() + " " + size.value;
+ return value;
+};
+initData();
+var docid = $id();
+
diff --git a/app/components/datatable/DataModel.vue b/app/components/datatable/DataModel.vue
index 65f3ccc..55df497 100644
--- a/app/components/datatable/DataModel.vue
+++ b/app/components/datatable/DataModel.vue
@@ -1,193 +1,314 @@
-
-
+
\ No newline at end of file
+
diff --git a/app/components/datatable/DataTable.vue b/app/components/datatable/DataTable.vue
index 4c1731f..cd406d1 100644
--- a/app/components/datatable/DataTable.vue
+++ b/app/components/datatable/DataTable.vue
@@ -1,57 +1,104 @@
-
-
-
-
-
- {{totalRows}}
-
-
-
-
-
- {{v.sort? v.sort : (v.select? ('[' + (v.select.length>0? $stripHtml(v.select[0],20) : '') + '...Σ' + v.select.length + ']') :
- (v.condition))}}
-
+
+
+
+
+
+ {{ totalRows }}
+
+
+
+
-
-
-
-
- |
-
- |
-
-
-
-
- |
-
- {{ v[field.name] }}
- |
-
-
+
+ {{
+ v.sort
+ ? v.sort
+ : v.select
+ ? "[" + (v.select.length > 0 ? $stripHtml(v.select[0], 20) : "") + "...Σ" + v.select.length + "]"
+ : v.condition
+ }}
+
+
+
+
+
+
+ |
+
+ |
+
+
+
+
+ |
+
+ {{ v[field.name] }}
+ |
+
+
store[props.pagename],
(newVal, oldVal) => {
updateChange();
- }
+ },
);
function updateChange() {
pagedata = store[props.pagename];
@@ -141,24 +188,21 @@ function updateChange() {
const updateShow = function (full_data) {
// allowed JS expressions - should return a boolean
const allowedFns = {
- '$getEditRights()': $getEditRights,
+ "$getEditRights()": $getEditRights,
};
- const arr = pagedata.fields.filter(({ show }) => {
- if (typeof show === 'boolean') return show;
+ const arr = pagedata.fields.filter(({ show }) => {
+ if (typeof show === "boolean") return show;
else {
// show is a string
- if (show === 'true') return true;
- if (show === 'false') return false;
+ if (show === "true") return true;
+ if (show === "false") return false;
return allowedFns[show]?.() || false;
}
});
if (full_data === false) displayData = $copy(data);
else
displayData = $copy(
- data.filter(
- (ele, index) =>
- index >= (currentPage - 1) * perPage && index < currentPage * perPage
- )
+ data.filter((ele, index) => index >= (currentPage - 1) * perPage && index < currentPage * perPage),
);
displayData.map((v) => {
arr.map((x) => (v[`${x.name}color`] = getStyle(x, v)));
@@ -222,9 +266,7 @@ const getStyle = function (field, record) {
field.bgcolor.map((v) => {
if (v.type === "search") {
if (
- record[field.name] && !stop
- ? record[field.name].toLowerCase().indexOf(v.keyword.toLowerCase()) >= 0
- : false
+ record[field.name] && !stop ? record[field.name].toLowerCase().indexOf(v.keyword.toLowerCase()) >= 0 : false
) {
val += ` background-color:${v.color}; `;
stop = true;
@@ -245,9 +287,7 @@ const getStyle = function (field, record) {
field.color.map((v) => {
if (v.type === "search") {
if (
- record[field.name] && !stop
- ? record[field.name].toLowerCase().indexOf(v.keyword.toLowerCase()) >= 0
- : false
+ record[field.name] && !stop ? record[field.name].toLowerCase().indexOf(v.keyword.toLowerCase()) >= 0 : false
) {
val += ` color:${v.color}; `;
stop = true;
@@ -268,9 +308,7 @@ const getStyle = function (field, record) {
field.textsize.map((v) => {
if (v.type === "search") {
if (
- record[field.name] && !stop
- ? record[field.name].toLowerCase().indexOf(v.keyword.toLowerCase()) >= 0
- : false
+ record[field.name] && !stop ? record[field.name].toLowerCase().indexOf(v.keyword.toLowerCase()) >= 0 : false
) {
val += ` font-size:${v.size}px; `;
stop = true;
@@ -283,10 +321,7 @@ const getStyle = function (field, record) {
}
}
});
- } else
- val += ` font-size:${
- tablesetting.find((v) => v.code === "table-font-size").detail
- }px;`;
+ } else val += ` font-size:${tablesetting.find((v) => v.code === "table-font-size").detail}px;`;
if (field.textalign) val += ` text-align:${field.textalign}; `;
if (field.minwidth) val += ` min-width:${field.minwidth}px; `;
if (field.maxwidth) val += ` max-width:${field.maxwidth}px; `;
@@ -295,56 +330,28 @@ const getStyle = function (field, record) {
const getSettingStyle = function (name, field) {
let value = "";
if (name === "container") {
- value =
- "min-height:" +
- tablesetting.find((v) => v.code === "container-height").detail +
- "rem; ";
+ value = "min-height:" + tablesetting.find((v) => v.code === "container-height").detail + "rem; ";
} else if (name === "table") {
- value +=
- "background-color:" +
- tablesetting.find((v) => v.code === "table-background").detail +
- "; ";
- value +=
- "font-size:" +
- tablesetting.find((v) => v.code === "table-font-size").detail +
- "px;";
- value +=
- "color:" + tablesetting.find((v) => v.code === "table-font-color").detail + "; ";
+ value += "background-color:" + tablesetting.find((v) => v.code === "table-background").detail + "; ";
+ value += "font-size:" + tablesetting.find((v) => v.code === "table-font-size").detail + "px;";
+ value += "color:" + tablesetting.find((v) => v.code === "table-font-color").detail + "; ";
} else if (name === "header") {
- value +=
- "background-color:" +
- tablesetting.find((v) => v.code === "header-background").detail +
- "; ";
+ value += "background-color:" + tablesetting.find((v) => v.code === "header-background").detail + "; ";
if (field.minwidth) value += " min-width: " + field.minwidth + "px; ";
if (field.maxwidth) value += " max-width: " + field.maxwidth + "px; ";
} else if (name === "menu") {
let arg = tablesetting.find((v) => v.code === "menu-width").detail;
arg = field ? (field.menuwidth ? field.menuwidth : arg) : arg;
value += "width:" + arg + "rem; ";
- value +=
- "min-height:" +
- tablesetting.find((v) => v.code === "menu-min-height").detail +
- "rem; ";
- value +=
- "max-height:" +
- tablesetting.find((v) => v.code === "menu-max-height").detail +
- "rem; ";
+ value += "min-height:" + tablesetting.find((v) => v.code === "menu-min-height").detail + "rem; ";
+ value += "max-height:" + tablesetting.find((v) => v.code === "menu-max-height").detail + "rem; ";
value += "overflow:auto; ";
} else if (name === "dropdown") {
- value +=
- "font-size:" +
- tablesetting.find((v) => v.code === "header-font-size").detail +
- "px; ";
+ value += "font-size:" + tablesetting.find((v) => v.code === "header-font-size").detail + "px; ";
let found = filters.find((v) => v.name === field.name);
found
- ? (value +=
- "color:" +
- tablesetting.find((v) => v.code === "header-filter-color").detail +
- "; ")
- : (value +=
- "color:" +
- tablesetting.find((v) => v.code === "header-font-color").detail +
- "; ");
+ ? (value += "color:" + tablesetting.find((v) => v.code === "header-filter-color").detail + "; ")
+ : (value += "color:" + tablesetting.find((v) => v.code === "header-font-color").detail + "; ");
}
return value;
};
@@ -369,12 +376,8 @@ const frontendFilter = function (newVal) {
else {
let text = "";
filter.map((y, k) => {
- text += `${
- k > 0 ? (filter[k - 1].operator === "and" ? " &&" : " ||") : ""
- } ${$formatNumber(x[name])}
- ${
- y.condition === "=" ? "==" : y.condition === "<>" ? "!==" : y.condition
- } ${$formatNumber(y.value)}`;
+ text += `${k > 0 ? (filter[k - 1].operator === "and" ? " &&" : " ||") : ""} ${$formatNumber(x[name])}
+ ${y.condition === "=" ? "==" : y.condition === "<>" ? "!==" : y.condition} ${$formatNumber(y.value)}`;
});
return $calc(text);
}
@@ -385,11 +388,7 @@ const frontendFilter = function (newVal) {
.filter((m) => m.select || m.filter)
.map((v) => {
if (v.select) {
- data = data.filter(
- (x) =>
- v.select.findIndex((y) => ($empty(y) ? $empty(x[v.name]) : y === x[v.name])) >
- -1
- );
+ data = data.filter((x) => v.select.findIndex((y) => ($empty(y) ? $empty(x[v.name]) : y === x[v.name])) > -1);
} else if (v.filter) {
data = data.filter((x) => checkValid(v.name, x, v.filter));
}
@@ -503,9 +502,7 @@ const updateData = async function (newVal) {
}
tablesetting = $copy(pagedata.tablesetting || gridsetting);
if (tablesetting) {
- perPage = pagedata.perPage
- ? pagedata.perPage
- : Number(tablesetting.find((v) => v.code === "per-page").detail);
+ perPage = pagedata.perPage ? pagedata.perPage : Number(tablesetting.find((v) => v.code === "per-page").detail);
}
if (newVal.fields) {
fields = $copy(newVal.fields);
diff --git a/app/components/datatable/DataView.vue b/app/components/datatable/DataView.vue
index ed68e58..f90805b 100644
--- a/app/components/datatable/DataView.vue
+++ b/app/components/datatable/DataView.vue
@@ -1,20 +1,46 @@
-
-
+ v-bind="{
+ pagename: vpagename,
+ api: api,
+ timeopt: timeopt,
+ filter: optfilter,
+ importdata: props.importdata,
+ newDataAvailable: newDataAvailable,
+ params: vparams,
+ }"
+ ref="timeopt"
+ @option="timeOption"
+ @excel="exportExcel"
+ @add="insert"
+ @manual-refresh="manualRefresh"
+ @refresh-data="refreshData"
+ @import="openImportModal"
+ class="mb-3"
+ v-if="timeopt"
+ >
+
+
\ No newline at end of file
+if (!props.timeopt) await getApi();
+startAutoCheck();
+
diff --git a/app/components/datatable/EditLabel.vue b/app/components/datatable/EditLabel.vue
index ae37aa6..b55e95d 100644
--- a/app/components/datatable/EditLabel.vue
+++ b/app/components/datatable/EditLabel.vue
@@ -1,82 +1,111 @@
Điều chỉnh tiêu đề
-
-
Dòng thứ {{(i+1)}} *
-
-
{{v.error}}
-
-
- Cập nhật
- Hủy bỏ
-
+
+
Dòng thứ {{ i + 1 }} *
+
+
+ {{ v.error }}
+
+
+
+
+ Cập nhật
+
+
+ Hủy bỏ
+
+
\ No newline at end of file
+ if (this.checkError()) return;
+ let label = "";
+ if (this.arr.length > 1) {
+ this.arr.map((v, i) => {
+ label += `${v.label.trim()}
`;
+ });
+ label = `${label}
`;
+ } else label = this.arr[0].label.trim();
+ this.$emit("modalevent", { name: "label", data: label });
+ this.$emit("close");
+ },
+ },
+};
+
diff --git a/app/components/datatable/FieldAttribute.vue b/app/components/datatable/FieldAttribute.vue
index 0103ac4..0521941 100644
--- a/app/components/datatable/FieldAttribute.vue
+++ b/app/components/datatable/FieldAttribute.vue
@@ -1,88 +1,127 @@