Files
web/app/plugins/01-common.js
2026-06-12 10:15:16 +07:00

665 lines
20 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useRoute } from "vue-router";
import dayjs from "dayjs";
import weekday from "dayjs/plugin/weekday";
import weekOfYear from "dayjs/plugin/weekOfYear";
import relativeTime from "dayjs/plugin/relativeTime";
import localizedFormat from "dayjs/plugin/localizedFormat";
import isSameOrBefore from "dayjs/plugin/isSameOrBefore";
import isSameOrAfter from "dayjs/plugin/isSameOrAfter";
import "dayjs/locale/vi";
import { deburr } from "es-toolkit";
dayjs.extend(weekday);
dayjs.extend(weekOfYear);
dayjs.extend(relativeTime);
dayjs.extend(localizedFormat);
dayjs.extend(isSameOrBefore);
dayjs.extend(isSameOrAfter);
dayjs.locale("vi");
export default defineNuxtPlugin((nuxtApp) => {
const route = useRoute();
const { $id, $empty, $store } = nuxtApp;
const dialog = function (content, title, type, duration, width, height, vbind) {
content = typeof content === "string" ? content : JSON.stringify(content);
const vtitle =
type === "Success"
? `<span class="has-text-primary">${title}</span>`
: type === "Error"
? `<span class="has-text-danger">${title}</span>`
: title;
const data = {
id: $id(),
component: `dialog/${type || "Info"}`,
vbind: { content, duration, vbind },
title: vtitle,
width: width || "600px",
height: height || "100px",
};
$store.commit("showmodal", data);
};
const snackbar = function (content, component = "Info") {
$store.commit("snackbar", {
component,
vbind: {
content: typeof content === "string" ? content : JSON.stringify(content),
},
});
};
const getLink = function (val) {
if ($empty(val)) return "";
const json = val.indexOf("{") >= 0 ? JSON.parse(val) : { path: val };
return json;
};
const errPhone = function (phone) {
let text;
if ($empty(phone)) {
text = "Số điện thoại di động không được bỏ trống.";
} else if (isNaN(phone)) {
text = "Số điện thoại di động không hợp lệ.";
} else if (phone.length < 9 || phone.length > 11) {
text = "Số điện thoại di động phải từ 9-11 số.";
}
return text;
};
const errEmail = function (email) {
const re =
/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
let text = undefined;
if ($empty(email)) {
text = "Email không được bỏ trống.";
} else if (!re.test(String(email).toLowerCase())) {
text = "Email không hợp lệ.";
}
return text;
};
const errPhoneEmail = function (contact) {
const re =
/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
let text = undefined;
if ($empty(contact)) {
text = "Số điện thoại di động hoặc Email không được bỏ trống.";
} else if (!(re.test(String(contact).toLowerCase()) || !isNaN(contact))) {
text = "Số điện thoại di động hoặc Email không hợp lệ.";
} else if (!isNaN(contact) && (contact.length < 9 || contact.length > 11)) {
text = "Số điện thoại di động không hợp lệ.";
}
return text;
};
const dummy = function (data, count) {
let list = this.$copy(data);
for (let index = 0; index < count; index++) {
if (data.length < index + 1) list.push({ dummy: true });
}
return list;
};
const upload = function (file, type, user, convert, quality) {
const fileFormat = [
{
type: "image",
format: [".png", ".jpg", "jpeg", ".bmp", ".gif", ".svg", ".webp"],
},
{
type: "video",
format: [".wmv", ".avi", ".mp4", ".flv", ".mov", ".mpg", ".amv", ".rm"],
},
];
let valid = undefined;
if (type === "image" || type === "video") {
valid = false;
let found = fileFormat.find((v) => v.type === type);
found.format.map((x) => {
if (file.name.toLowerCase().indexOf(x) >= 0) valid = true;
});
}
if (valid === false)
return {
name: file.name,
error: true,
text: "Định dạng file không hợp lệ",
};
if ((type === "image" || type === "file") && file.size > 500 * 1024 * 1024) {
return {
name: file.name,
error: true,
text: "Kích thước " + (type === "image" ? "hình ảnh" : "tài liệu") + " phải dưới 500MB",
};
} else if (type === "video" && file.size > 1073741274) {
return {
name: file.name,
error: true,
text: "Kích thước video phải dưới 1GB",
};
}
const form = new FormData();
const filename = dayjs(new Date()).format("YYYYMMDDhhmmss") + "-" + file.name;
form.append("name", file.name);
form.append("filename", filename);
form.append("file", file);
form.append("type", type);
form.append("size", file.size);
form.append("user", user);
form.append("convert", convert);
// Thêm quality nếu convert được bật và quality được cung cấp
if (convert && quality !== null && quality !== undefined) {
form.append("quality", quality);
}
return {
form,
type,
file,
filename,
name: file.name,
size: file.size,
};
};
const change = function (obj1, obj2, list) {
var change = false;
if (list) {
list.map((v) => {
if (obj1[v] !== obj2[v]) change = true;
});
} else {
for (var k in obj1) {
if (obj1[k] !== obj2[k]) change = true;
}
}
return change;
};
const resetNull = function (obj) {
return Object.fromEntries(
Object.entries(obj).map(([key, value]) => {
if (typeof value === "string") {
const val = value.trim();
return [key, val === "" ? null : val];
}
return [key, value];
}),
);
};
const copyToClipboard = function (text) {
snackbar("Copied to clipboard", "Success");
if (window.clipboardData && window.clipboardData.setData) {
// IE specific code path to prevent textarea being shown while dialog is visible.
return clipboardData.setData("Text", text);
} else if (document.queryCommandSupported && document.queryCommandSupported("copy")) {
var textarea = document.createElement("textarea");
textarea.textContent = text;
textarea.style.position = "fixed"; // Prevent scrolling to bottom of page in MS Edge.
document.body.appendChild(textarea);
textarea.select();
try {
return document.execCommand("copy"); // Security exception may be thrown by some browsers.
} catch (ex) {
console.warn("Copy to clipboard failed.", ex);
return false;
} finally {
document.body.removeChild(textarea);
}
}
};
const nonAccent = function (str) {
if ($empty(str)) return "";
const result = deburr(str)
.toLowerCase()
.replaceAll(/\u02C6/g, "") // 'ˆ' (Some system encode vietnamese combining accent as individual utf-8 characters)
.replaceAll(/[\/%?]/g, " ")
.replaceAll(/[\s-]+/g, "-");
return result;
};
const linkID = function (link) {
link = link ? link : route.params.slug;
if ($empty(link)) return;
let idx = link.lastIndexOf("-");
let id = idx > -1 && idx < link.length - 1 ? link.substring(idx + 1, link.length) : undefined;
return id;
};
const exportpdf = function (docid, name, size, orientation = "portrait") {
var element = document.getElementById(docid);
let elms = element.querySelectorAll("[id='ignore']");
for (var i = 0; i < elms.length; i++) {
elms[i].style.setProperty("display", "none", "important");
}
// Xóa cache ảnh để lần export sau không bị lỗi
element.querySelectorAll("img").forEach((img) => {
img.src = `${img.src}`;
});
var opt = {
margin: 0.4,
filename: `${name || "file"}-${dayjs().format("YYYYMMDDHHmmss")}.pdf`,
image: { type: "jpeg", quality: 1 },
html2canvas: { scale: 3, useCORS: true },
jsPDF: { unit: "in", format: size || "a4", orientation },
pagebreak: {
mode: ["avoid-all", "css", "legacy"],
before: ".page-break-before",
after: ".page-break-after",
avoid: ".avoid-page-break",
},
};
setTimeout(() => html2pdf().set(opt).from(element).save(), 300);
setTimeout(() => {
for (var i = 0; i < elms.length; i++) {
elms[i].style.removeProperty("display");
}
}, 1000);
return opt.filename;
};
const exportImage = function (docid, name) {
var element = document.getElementById(docid);
let elms = element.querySelectorAll("[id='ignore']");
for (var i = 0; i < elms.length; i++) {
elms[i].style.display = "none";
}
let filename = `${name}-${dayjs().format("YYYYMMDDHHmmss")}.png`;
html2canvas(element).then((canvas) => {
const link = document.createElement("a");
link.href = canvas.toDataURL("image/png");
link.download = filename;
link.click();
link.remove();
});
setTimeout(() => {
for (var i = 0; i < elms.length; i++) {
elms[i].style.display = "block";
}
}, 1000);
return filename;
};
const download = async function (url, fileName) {
const name = dayjs(new Date()).format("YYYYMMDDhhmmss") + "-" + fileName;
const response = await fetch(url);
const blob = await response.blob();
const urlDownload = window.URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = urlDownload;
link.setAttribute("download", name);
link.click();
link.remove();
};
const vnmoney = function (amount) {
var readGroup = function (group) {
let readDigit = [" Không", " Một", " Hai", " Ba", " Bốn", " Năm", " Sáu", " Bảy", " Tám", " Chín"];
var temp = "";
if (group == "000") return "";
temp = readDigit[parseInt(group.substring(0, 1))] + " Trăm";
if (group.substring(1, 2) == "0")
if (group.substring(2, 3) == "0") return temp;
else {
temp += " Lẻ" + readDigit[parseInt(group.substring(2, 3))];
return temp;
}
else temp += readDigit[parseInt(group.substring(1, 2))] + " Mươi";
if (group.substring(2, 3) == "5") temp += " Lăm";
else if (group.substring(2, 3) != "0") temp += readDigit[parseInt(group.substring(2, 3))];
return temp;
};
var readMoney = function (num) {
if (num == null || num == "") return "";
let temp = "";
while (num.length < 18) {
num = "0" + num;
}
let g1 = num.substring(0, 3);
let g2 = num.substring(3, 6);
let g3 = num.substring(6, 9);
let g4 = num.substring(9, 12);
let g5 = num.substring(12, 15);
let g6 = num.substring(15, 18);
if (g1 != "000") {
temp = readGroup(g1);
temp += " Triệu";
}
if (g2 != "000") {
temp += readGroup(g2);
temp += " Nghìn";
}
if (g3 != "000") {
temp += readGroup(g3);
temp += " Tỷ";
} else if ("" != temp) {
temp += " Tỷ";
}
if (g4 != "000") {
temp += readGroup(g4);
temp += " Triệu";
}
if (g5 != "000") {
temp += readGroup(g5);
temp += " Nghìn";
}
temp = temp + readGroup(g6);
temp = temp.replaceAll("Một Mươi", "Mười");
temp = temp.trim();
temp = temp.replaceAll("Không Trăm", "");
temp = temp.trim();
temp = temp.replaceAll("Mười Không", "Mười");
temp = temp.trim();
temp = temp.replaceAll("Mươi Không", "Mươi");
temp = temp.trim();
if (temp.indexOf("Lẻ") == 0) temp = temp.substring(2);
temp = temp.trim();
temp = temp.replaceAll("Mươi Một", "Mươi Mốt");
temp = temp.trim();
let result = temp.substring(0, 1).toUpperCase() + temp.substring(1).toLowerCase();
return (result == "" ? "Không" : result) + " đồng chẵn";
};
return readMoney(amount.toString());
};
const lang = function (code) {
let field = $store.common.find((v) => v.code === code);
return field ? field[$store.lang] : "";
};
const createMeta = function (metainfo) {
return {
title: metainfo.title,
link: [
{
hid: "canonical",
rel: "canonical",
href: `https://bigdatatech.vn${route.path}`,
},
],
meta: [
{
hid: "description",
name: "description",
content: metainfo.description || "",
},
{
hid: "og:title",
property: "og:title",
content: metainfo.title,
},
{
hid: "og:description",
property: "og:description",
content: metainfo.description || "",
},
{
hid: "og:type",
property: "og:type",
content: metainfo.type || "",
},
{
hid: "og:image",
property: "og:image",
content: metainfo.image || "",
},
{
hid: "og:url",
property: "og:url",
content: `https://bigdatatech.vn${route.path}`,
},
{
property: "og:locale",
content: "vi_VN",
},
{
hid: "fb:app_id",
property: "fb:app_id",
content: metainfo.app_id || "549555567061256",
},
{
hid: "keywords",
name: "keywords",
content: metainfo.keywords || "",
},
],
};
};
const formatFileSize = (bytes) => {
if (bytes === 0) return "0 Bytes";
const k = 1024;
const sizes = ["Bytes", "KB", "MB", "GB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + " " + sizes[i];
};
const numberToVietnamese = (input) => {
if (input === null || input === undefined || input === "") return "";
var number = Number(input);
if (isNaN(number) || number < 0 || number % 1 !== 0) return "";
if (number === 0) return "Không";
var units = ["không", "một", "hai", "ba", "bốn", "năm", "sáu", "bảy", "tám", "chín"];
var scales = ["", "nghìn", "triệu", "tỷ", "nghìn tỷ", "triệu tỷ"];
function readThreeDigits(num, isFull) {
var result = "";
var hundred = Math.floor(num / 100);
var ten = Math.floor((num % 100) / 10);
var unit = num % 10;
if (hundred > 0 || isFull) {
result += units[hundred] + " trăm";
if (ten === 0 && unit > 0) result += " lẻ";
}
if (ten > 1) {
result += " " + units[ten] + " mươi";
if (unit === 1) result += " mốt";
else if (unit === 5) result += " lăm";
else if (unit > 0) result += " " + units[unit];
} else if (ten === 1) {
result += " mười";
if (unit === 5) result += " lăm";
else if (unit > 0) result += " " + units[unit];
} else if (ten === 0 && unit > 0 && hundred === 0 && !isFull) {
result += units[unit];
}
return result.trim();
}
var result = "";
var scaleIndex = 0;
while (number > 0) {
var chunk = number % 1000;
if (chunk > 0) {
var isFull = result !== "";
result =
readThreeDigits(chunk, isFull) +
(scales[scaleIndex] ? " " + scales[scaleIndex] : "") +
(result ? " " + result : "");
}
number = Math.floor(number / 1000);
scaleIndex++;
}
result = result.trim();
return result.charAt(0).toUpperCase() + result.slice(1);
};
const formatDateVN = (input, { withTime = false, withSeconds = false } = {}) => {
if (!input) return "";
const date = new Date(input);
if (isNaN(date.getTime())) return "";
const pad = (n) => String(n).padStart(2, "0");
const day = pad(date.getDate());
const month = pad(date.getMonth() + 1);
const year = date.getFullYear();
let result = `${day}/${month}/${year}`;
if (withTime) {
const hours = pad(date.getHours());
const minutes = pad(date.getMinutes());
result += ` ${hours}:${minutes}`;
if (withSeconds) {
const seconds = pad(date.getSeconds());
result += `:${seconds}`;
}
}
return result;
};
const numberToVietnameseCurrency = (amount) => {
if (amount === null || amount === undefined || amount === "") return "";
amount = Number(amount);
if (isNaN(amount)) return "";
const units = ["", "một", "hai", "ba", "bốn", "năm", "sáu", "bảy", "tám", "chín"];
const scales = ["", "nghìn", "triệu", "tỷ"];
function readThreeDigits(num) {
let result = "";
const hundred = Math.floor(num / 100);
const ten = Math.floor((num % 100) / 10);
const unit = num % 10;
if (hundred > 0) {
result += units[hundred] + " trăm";
if (ten === 0 && unit > 0) result += " lẻ";
}
if (ten > 1) {
result += " " + units[ten] + " mươi";
if (unit === 1) result += " mốt";
else if (unit === 5) result += " lăm";
else if (unit > 0) result += " " + units[unit];
} else if (ten === 1) {
result += " mười";
if (unit === 5) result += " lăm";
else if (unit > 0) result += " " + units[unit];
} else if (ten === 0 && unit > 0 && hundred === 0) {
result += units[unit];
}
return result.trim();
}
let text = "";
let scaleIndex = 0;
while (amount > 0) {
const chunk = amount % 1000;
if (chunk > 0) {
text = `${readThreeDigits(chunk)} ${scales[scaleIndex]} ${text}`;
}
amount = Math.floor(amount / 1000);
scaleIndex++;
}
text = text.trim();
text = text.charAt(0).toUpperCase() + text.slice(1);
return text;
};
const getFirstAndLastName = (fullName, showDots) => {
if (!fullName) return "";
var parts = fullName.trim().replace(/\s+/g, " ").split(" ");
if (parts.length === 1) return parts[0];
var first = parts[0];
var last = parts[parts.length - 1];
return showDots ? first + "..." + last : first + " " + last;
};
const paymentQR = (content = "", bankCode = "MB", amount = 0) => {
const listBanks = [
{
bank: {
code: "MB",
name: "MB Bank",
},
account: {
number: "146768686868",
name: "CONG TY CO PHAN BAT DONG SAN UTOPIA",
},
content: "Thanh toán hóa đơn",
},
];
let bankInfo = listBanks.find(
(item) => item.bank.code && bankCode && item.bank.code.toLowerCase() === bankCode.toLowerCase(),
);
if (!bankInfo) return;
const params = new URLSearchParams({
addInfo: content || bankInfo.content,
accountName: bankInfo.account.name || "",
amount: amount || 0,
});
return `https://img.vietqr.io/image/${bankInfo.bank.code}-${bankInfo.account.number}-print.png?${params.toString()}`;
};
function shortenCurrency(amount) {
return new Intl.NumberFormat("en", { notation: "compact" }).format(Number(amount));
}
return {
provide: {
dialog,
snackbar,
getLink,
errPhone,
errEmail,
errPhoneEmail,
dummy,
upload,
change,
resetNull,
copyToClipboard,
nonAccent,
linkID,
exportpdf,
exportImage,
vnmoney,
createMeta,
dayjs,
download,
lang,
formatFileSize,
numberToVietnamese,
numberToVietnameseCurrency,
formatDateVN,
getFirstAndLastName,
paymentQR,
shortenCurrency,
},
};
});