Base Login

This commit is contained in:
ThienPhamVan
2026-03-25 10:06:01 +07:00
commit 3a2e16cf19
81 changed files with 27983 additions and 0 deletions

301
plugins/common.js Normal file
View File

@@ -0,0 +1,301 @@
import Vue from 'vue';
import { COMPANY } from '~/constants/company';
Vue.use({
install(Vue) {
Vue.prototype.$dialog = function (content, title, type, duration, width, height, vbind) {
if (typeof content == 'string') {
let vtitle = type === 'Success' ? `<span class="has-text-primary">${title}</span>` : title;
if (type === 'Error') vtitle = `<span class="has-text-danger">${title}</span>`;
let data = {
id: this.$id(),
component: `dialog/${type || 'Info'}`,
vbind: { content: content, duration: duration, vbind: vbind },
title: vtitle,
width: width || '500px',
height: height || '100px',
};
this.$store.commit('updateStore', { name: 'showmodal', data: data });
} else this.$store.commit('updateStore', { name: 'showmodal', data: content });
};
Vue.prototype.$snackbar = function (content, title, type, width, height) {
if (typeof content == 'string') {
let vtitle = type === 'Success' ? `<span class="has-text-primary">${title}</span>` : title;
if (type === 'Error') vtitle = `<span class="has-text-danger">${title}</span>`;
let data = {
id: this.$id(),
component: `snackbar/${type || 'Info'}`,
vbind: { content: content },
title: vtitle,
width: width || '400px',
height: height || '100px',
};
this.$store.commit('updateStore', { name: 'snackbar', data: data });
} else this.$store.commit('updateStore', { name: 'snackbar', data: content });
};
Vue.prototype.$pending = function () {
this.$dialog({
width: '500px',
icon: ' mdi mdi-wrench-clock',
content: '<p class="fs-16">Chức năng này đang được xây dựng, vui lòng trở lại sau</p>',
type: 'is-dark',
progress: true,
duration: 5,
});
};
Vue.prototype.$getLink = function (val) {
if (val === undefined || val === null || val === '' || val === '') return '';
let json = val.indexOf('{') >= 0 ? JSON.parse(val) : { path: val };
return json;
};
Vue.prototype.$timeFormat = function (startDate, endDate) {
let milliseconds = startDate - endDate;
let secs = Math.floor(Math.abs(milliseconds) / 1000);
let mins = Math.floor(secs / 60);
let hours = Math.floor(mins / 60);
let days = Math.floor(hours / 24);
const millisecs = Math.floor(Math.abs(milliseconds)) % 1000;
function pad2(n) {
return (n < 10 ? '0' : '') + n;
}
let display = undefined;
if (days >= 1) {
display =
pad2(startDate.getHours()) +
':' +
pad2(startDate.getMinutes()) +
' ' +
pad2(startDate.getDate()) +
'/' +
pad2(startDate.getMonth());
} else if (hours > 0) display = hours + 'h trước';
else if (mins > 0) display = mins + "' trước";
else if (secs > 0 || millisecs > 0) display = 'Vừa xong';
return {
days: days,
hours: hours % 24,
minutes: mins % 60,
seconds: secs % 60,
milliSeconds: millisecs,
display: display,
};
};
(Vue.prototype.$errPhone = function (phone) {
var text = undefined;
if (this.$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 có từ 9-11 số.';
}
return text;
}),
(Vue.prototype.$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,}))$/;
var text = undefined;
if (this.$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;
});
Vue.prototype.$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,}))$/;
var text = undefined;
if (this.$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;
};
Vue.prototype.$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;
};
Vue.prototype.$upload = function (file, type, user) {
var fileFormat = [
{ type: 'image', format: ['.png', '.jpg', 'jpeg', '.bmp', '.gif', '.svg'] },
{ type: 'video', format: ['.wmv', '.avi', '.mp4', '.flv', '.mov', '.mpg', '.amv', '.rm'] },
];
var 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 { error: true, text: 'Định dạng file không hợp lệ' };
if ((type === 'image' || type === 'file') && file.size > 500 * 1024 * 1024) {
return { 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 { error: true, text: 'Kích thước video phải dưới 1GB' };
}
let data = new FormData();
let fileName = this.$dayjs(new Date()).format('YYYYMMDDhhmmss') + '-' + file.name;
data.append('name', fileName);
data.append('file', file);
data.append('type', type);
data.append('size', file.size);
data.append('user', user);
return { form: data, name: fileName, type: type, size: file.size, file: file };
};
Vue.prototype.$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;
};
Vue.prototype.$resetNull = function (obj) {
for (var key in obj) {
if (obj[key] === '' || obj[key] === '') obj[key] = null;
}
return obj;
};
Vue.prototype.$responsiveMenu = function () {
// Get all "navbar-burger" elements
const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0);
// Check if there are any navbar burgers
if ($navbarBurgers.length > 0) {
// Add a click event on each of them
$navbarBurgers.forEach((el) => {
// Get the target from the "data-target" attribute
const target = el.dataset.target;
const $target = document.getElementById(target);
// Toggle the "is-active" class on both the "navbar-burger" and the "navbar-menu"
el.classList.toggle('is-active');
$target.classList.toggle('is-active');
});
}
};
Vue.prototype.$copyToClipboard = function (text) {
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);
}
}
};
Vue.prototype.$nonAccent = function (str) {
if (this.$empty(str)) return null;
str = str.replaceAll('/', '-').replaceAll('%', '-').replaceAll('?', '-');
str = str.toLowerCase();
str = str.replace(/à|á|ạ|ả|ã|â|ầ|ấ|ậ|ẩ|ẫ|ă|ằ|ắ|ặ|ẳ|ẵ/g, 'a');
str = str.replace(/è|é|ẹ|ẻ|ẽ|ê|ề|ế|ệ|ể|ễ/g, 'e');
str = str.replace(/ì|í|ị|ỉ|ĩ/g, 'i');
str = str.replace(/ò|ó|ọ|ỏ|õ|ô|ồ|ố|ộ|ổ|ỗ|ơ|ờ|ớ|ợ|ở|ỡ/g, 'o');
str = str.replace(/ù|ú|ụ|ủ|ũ|ư|ừ|ứ|ự|ử|ữ/g, 'u');
str = str.replace(/ỳ|ý|ỵ|ỷ|ỹ/g, 'y');
str = str.replace(/đ/g, 'd');
// Some system encode vietnamese combining accent as individual utf-8 characters
str = str.replace(/\u0300|\u0301|\u0303|\u0309|\u0323/g, ''); // Huyền sắc hỏi ngã nặng
str = str.replace(/\u02C6|\u0306|\u031B/g, ''); // Â, Ê, Ă, Ơ, Ư
str = str
.split(' ')
.filter((s) => s)
.join('-');
return str;
};
Vue.prototype.$linkID = function (link) {
link = link ? link : this.$route.params.slug;
if (this.$empty(link)) return;
let idx = link.lastIndexOf('-');
let id = idx > -1 && idx < link.length - 1 ? link.substring(idx + 1, link.length) : undefined;
return id;
};
Vue.prototype.$redirectWeb = function (ele) {
if (this.$store.state.iframe) {
let info = { id: ele.id, email: ele.email, fullname: ele.fullname, avatar: ele.avatar, token: ele.token };
if (window.parent) window.parent.postMessage(JSON.stringify(info), '*');
return;
}
let link = this.$store.state.link || COMPANY.website;
window.location.href =
link +
'?email=' +
ele.email +
'&userid=' +
ele.id +
'&fullname=' +
ele.fullname +
(ele.avatar ? '&avatar=' + ele.avatar : '') +
'&token=' +
ele.token;
};
Vue.prototype.$companyInfo = function () {
return COMPANY;
};
Vue.prototype.$regexEmail = function (email) {
const regexEmail = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
return regexEmail.test(email);
};
Vue.prototype.$regexPassword = function (password, length = 8) {
const regexPass = new RegExp(`^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[^A-Za-z\\d]).{${length},}$`);
return regexPass.test(password);
};
Vue.prototype.$regexPhone = function (phone) {
const regexPhone = /^(0|\+84)(3|5|7|8|9)[0-9]{8}$/;
return regexPhone.test(phone);
};
Vue.prototype.$regexFullName = function (fullName) {
const regexFullName = /^[A-Za-zÀ-ỹ]+(\s[A-Za-zÀ-ỹ]+)+$/;
return regexFullName.test(fullName);
};
},
});