666 lines
22 KiB
Vue
666 lines
22 KiB
Vue
<template>
|
|
<div class="login-page">
|
|
<div class="columns is-centered px-0 mx-0 my-0">
|
|
<div :class="`column wrapper-login is-${viewport >= 4 ? 4 : 6}`">
|
|
<div class="px-6 py-6 login-box">
|
|
<div class="login-header">
|
|
<Logo />
|
|
<h2 class="title mt-4 has-text-centered">
|
|
{{ isVietnamese ? 'Đăng ký tài khoản' : 'Register an account' }}
|
|
</h2>
|
|
</div>
|
|
<div class="login-body">
|
|
<div class="form-login">
|
|
<div class="field mt-5 pt-1">
|
|
<label class="label">Họ và tên<b class="ml-1 has-text-danger">*</b></label>
|
|
<div class="control">
|
|
<input
|
|
class="input"
|
|
type="text"
|
|
placeholder=""
|
|
v-model="fullname"
|
|
@blur="validateFullName(fullname)"
|
|
/>
|
|
</div>
|
|
<p class="help is-danger" v-if="errors.find((v) => v.name === 'fullname')">
|
|
{{ errors.find((v) => v.name === 'fullname').text }}
|
|
</p>
|
|
</div>
|
|
<div class="field mt-4">
|
|
<label class="label">Điện thoại</label>
|
|
<div class="control">
|
|
<input class="input" type="text" placeholder="" v-model="phone" @blur="validatePhone(phone)" />
|
|
</div>
|
|
<p class="help is-danger" v-if="errors.find((v) => v.name === 'phone')">
|
|
{{ errors.find((v) => v.name === 'phone').text }}
|
|
</p>
|
|
</div>
|
|
<div class="field mt-4">
|
|
<label class="label">Email<b class="ml-1 has-text-danger">*</b></label>
|
|
<div class="control">
|
|
<input class="input" type="email" placeholder="" v-model="email" @blur="validateEmail(email)" />
|
|
</div>
|
|
<p class="help is-danger" v-if="errors.find((v) => v.name === 'email')">
|
|
{{ errors.find((v) => v.name === 'email').text }}
|
|
</p>
|
|
<p class="help is-primary" v-else-if="info">{{ info }}</p>
|
|
</div>
|
|
<div class="field mt-4">
|
|
<label class="label">Mật khẩu<b class="ml-1 has-text-danger">*</b></label>
|
|
<div class="field-body">
|
|
<div class="field has-addons">
|
|
<p class="control is-expanded">
|
|
<input
|
|
class="input"
|
|
:type="showpass ? 'text' : 'password'"
|
|
placeholder="Mật khẩu phải có ít nhất 8 ký tự, bao gồm chữ hoa, chữ thường, số và ký tự đặc biệt."
|
|
v-model="password"
|
|
@blur="validatePassword(password)"
|
|
/>
|
|
</p>
|
|
<div class="control">
|
|
<a class="button" @click="showpass = !showpass">
|
|
<span class="icon">
|
|
<i
|
|
:class="
|
|
showpass
|
|
? 'mdi mdi-eye-outline has-text-dark fs22'
|
|
: 'mdi mdi-eye-off-outline has-text-dark fs22'
|
|
"
|
|
/>
|
|
</span>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<p class="help is-danger" v-if="errors.find((v) => v.name === 'password')">
|
|
{{ errors.find((v) => v.name === 'password').text }}
|
|
</p>
|
|
</div>
|
|
<div class="field mt-4">
|
|
<label class="label">Nhập lại mật khẩu<b class="ml-1 has-text-danger">*</b></label>
|
|
<div class="field-body">
|
|
<div class="field">
|
|
<p class="control is-expanded">
|
|
<input
|
|
class="input"
|
|
:type="showpass ? 'text' : 'password'"
|
|
placeholder=""
|
|
v-model="retypePassword"
|
|
/>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<p class="help is-danger" v-if="errors.find((v) => v.name === 'retypePassword')">
|
|
{{ errors.find((v) => v.name === 'retypePassword').text }}
|
|
</p>
|
|
</div>
|
|
|
|
<div class="field mt-4">
|
|
<div class="control">
|
|
<b-checkbox v-model="checkbox"
|
|
>Tôi đồng ý với <a @click="$router.push('/policy')">các điều khoản và điều kiện</a></b-checkbox
|
|
>
|
|
</div>
|
|
<p class="help is-danger" v-if="errors.find((v) => v.name === 'checkbox')">
|
|
{{ errors.find((v) => v.name === 'checkbox').text }}
|
|
</p>
|
|
</div>
|
|
<div class="field mt-5 pt-1 group-action">
|
|
<button class="button is-primary" @click="createAccount()">Tạo tài khoản</button>
|
|
<!-- <a class="ml-2" @click="$router.push('/signin')">Đăng nhập</a> -->
|
|
</div>
|
|
</div>
|
|
<div class="social-login">
|
|
<div class="social-header my-4">
|
|
<span>Hoặc</span>
|
|
</div>
|
|
<div class="social-body">
|
|
<div class="social-item">
|
|
<GoogleLogin class="button" :onSuccess="onSuccess" :params="params">
|
|
<img class="icon" src="/icons/google.svg" alt="Google" />
|
|
<span>Đăng ký với Google</span>
|
|
</GoogleLogin>
|
|
</div>
|
|
<div class="social-item">
|
|
<button class="button" @click="logInWithFacebook()">
|
|
<img class="icon" src="/icons/facebook.svg" alt="Facebook" />
|
|
<span> Đăng ký với Facebook </span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<p class="mt-5 has-text-centered">
|
|
Bạn đã có tài khoản?
|
|
<nuxt-link class="ml-2" to="/signin">Đăng nhập</nuxt-link>
|
|
</p>
|
|
|
|
<div class="info-support mt-4">
|
|
<div class="support-header">
|
|
<h5 class="title">Cần hỗ trợ</h5>
|
|
</div>
|
|
<div class="support-body">
|
|
<p class="support-item">
|
|
<a :href="`tel:${company.phone.support}`" target="_blank">
|
|
<img src="/icons/phone-call.svg" alt="Support" width="24" height="24" />
|
|
<span>{{ company.phone.support }}</span>
|
|
</a>
|
|
</p>
|
|
<p class="support-item">
|
|
<a :href="`mailto:${company.email.support}`">
|
|
<img src="/icons/email.svg" alt="" width="24" height="24" />
|
|
<span>{{ company.email.support }}</span>
|
|
</a>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="login-footer">
|
|
<img src="/icons/shield.svg" alt="" width="24" />
|
|
|
|
<span> {{ new Date().getFullYear() }} </span>
|
|
<span v-if="isVietnamese">
|
|
Bản quyền thuộc về
|
|
<a
|
|
:href="company.parent.website"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
:title="company.parent.slogan"
|
|
>{{ company.parent.name }}</a
|
|
>
|
|
</span>
|
|
|
|
<span v-else>
|
|
Copyright by
|
|
<a
|
|
:href="company.parent.website"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
:title="company.parent.sloganEn"
|
|
>{{ company.parent.name }}</a
|
|
>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import GoogleLogin from 'vue-google-login';
|
|
export default {
|
|
components: { GoogleLogin },
|
|
head() {
|
|
return { title: `${this.isVietnamese ? 'Đăng ký tài khoản' : 'Register an account'} - ${this.company.name}` };
|
|
},
|
|
//
|
|
data() {
|
|
return {
|
|
fullname: undefined,
|
|
email: undefined,
|
|
password: undefined,
|
|
retypePassword: undefined,
|
|
errors: [],
|
|
info: 'Hệ thống gửi mã xác thực qua email. Sau khi tạo vui lòng kiểm tra email trong Inbox hoặc Spam',
|
|
showpass: false,
|
|
hash: undefined,
|
|
checkbox: false,
|
|
params: { client_id: '389310749372-mnfsd09ukgl0ahbdrm94dsepremlfgs9.apps.googleusercontent.com' },
|
|
type: undefined,
|
|
status: undefined,
|
|
user: undefined,
|
|
code: undefined,
|
|
isLoaded: false,
|
|
show: true,
|
|
discount: undefined,
|
|
authEmail: undefined,
|
|
phone: undefined,
|
|
validTo: this.$dayjs().add(30, 'day').format('DD/MM/YYYY'),
|
|
company: this.$companyInfo(),
|
|
isVietnamese: true,
|
|
};
|
|
},
|
|
async mounted() {
|
|
window.addEventListener('keyup', (ev) =>
|
|
ev.key === 'Enter' && this.$route.name === 'signup' ? this.createAccount() : false,
|
|
);
|
|
if (!this.$empty(this.$route.query.referer)) this.referer = this.$route.query.referer;
|
|
if (this.referer) this.discount = await this.$getdata('commission', { code: 'customer' }, undefined, true);
|
|
},
|
|
watch: {
|
|
isLoaded: function (newVal) {
|
|
if (newVal) {
|
|
let self = this;
|
|
window.FB.login(function (response) {
|
|
if (response.authResponse) {
|
|
let userId = response.authResponse.userID;
|
|
self.getUserInfo(userId);
|
|
}
|
|
});
|
|
}
|
|
},
|
|
},
|
|
computed: {
|
|
registermethod: {
|
|
get: function () {
|
|
return this.$store.state.registermethod;
|
|
},
|
|
set: function (val) {
|
|
this.$store.commit('updateRegisterMethod', { registermethod: val });
|
|
},
|
|
},
|
|
authmethod: {
|
|
get: function () {
|
|
return this.$store.state.authmethod;
|
|
},
|
|
set: function (val) {
|
|
this.$store.commit('updateAuthMethod', { authmethod: val });
|
|
},
|
|
},
|
|
authstatus: {
|
|
get: function () {
|
|
return this.$store.state.authstatus;
|
|
},
|
|
set: function (val) {
|
|
this.$store.commit('updateAuthStatus', { authstatus: val });
|
|
},
|
|
},
|
|
usertype: {
|
|
get: function () {
|
|
return this.$store.state.usertype;
|
|
},
|
|
set: function (val) {
|
|
this.$store.commit('updateUserType', { usertype: val });
|
|
},
|
|
},
|
|
dialog: {
|
|
get: function () {
|
|
return this.$store.state['dialog'];
|
|
},
|
|
set: function (val) {
|
|
this.$store.commit('updateStore', { name: 'dialog', data: val });
|
|
},
|
|
},
|
|
viewport: {
|
|
get: function () {
|
|
return this.$store.state.viewport;
|
|
},
|
|
set: function (val) {
|
|
this.$store.commit('updateViewPort', { viewport: val });
|
|
},
|
|
},
|
|
referer: {
|
|
get: function () {
|
|
return this.$store.state['referer'];
|
|
},
|
|
set: function (val) {
|
|
this.$store.commit('updateStore', { name: 'referer', data: val });
|
|
},
|
|
},
|
|
},
|
|
methods: {
|
|
checkError() {
|
|
this.errors = [];
|
|
if (!this.$empty(this.fullname)) {
|
|
this.fullname = this.fullname.trim();
|
|
}
|
|
if (!this.$empty(this.email)) {
|
|
this.email = this.email.trim().toLowerCase();
|
|
}
|
|
// if (this.$empty(this.fullname)) {
|
|
// this.errors.push({ name: 'fullname', text: 'Họ và tên không được bỏ trống.' });
|
|
// } else if (this.fullname.length < 5) {
|
|
// this.errors.push({ name: 'fullname', text: 'Họ và tên quá ngắn. Yêu cầu từ 5 kí tự trở nên.' });
|
|
// }
|
|
// if (!this.$empty(this.phone)) {
|
|
// if (!this.$regexPhone(this.phone)) {
|
|
// this.errors.push({ name: 'phone', text: 'Số điện thoại không hợp lệ.' });
|
|
// }
|
|
// }
|
|
this.validateFullName(this.fullname);
|
|
this.validatePhone(this.phone);
|
|
this.validateEmail(this.email);
|
|
this.validatePassword(this.password);
|
|
|
|
if (this.$empty(this.retypePassword)) {
|
|
this.errors.push({ name: 'retypePassword', text: 'Nhắc lại mật khẩu không được bỏ trống.' });
|
|
} else if (this.password !== this.retypePassword) {
|
|
this.errors.push({ name: 'retypePassword', text: 'Nhắc lại mật khẩu phải giống với mật khẩu đã nhập.' });
|
|
}
|
|
if (!this.checkbox)
|
|
this.errors.push({ name: 'checkbox', text: 'Bạn chưa chọn đồng ý với điều khoản và điều kiện của chúng tôi.' });
|
|
return this.errors.length > 0 ? true : false;
|
|
},
|
|
|
|
validateFullName(fullName) {
|
|
// Xóa lỗi cũ
|
|
this.errors = this.errors.filter((err) => err.name !== 'fullname');
|
|
|
|
const res = this.$validateFullName(fullName);
|
|
|
|
if (!res.status) {
|
|
this.errors.push({
|
|
name: 'fullname',
|
|
text: res.message,
|
|
});
|
|
}
|
|
},
|
|
|
|
validateEmail(email) {
|
|
// Xóa lỗi cũ
|
|
this.errors = this.errors.filter((err) => err.name !== 'email');
|
|
|
|
const res = this.$validateEmail(email);
|
|
|
|
if (!res.status) {
|
|
this.errors.push({
|
|
name: 'email',
|
|
text: res.message,
|
|
});
|
|
}
|
|
},
|
|
|
|
validatePassword(password) {
|
|
// Xóa lỗi cũ
|
|
this.errors = this.errors.filter((err) => err.name !== 'password');
|
|
|
|
const res = this.$validatePassword(password);
|
|
|
|
if (!res.status) {
|
|
this.errors.push({
|
|
name: 'password',
|
|
text: res.message,
|
|
});
|
|
}
|
|
},
|
|
|
|
validatePhone(phone) {
|
|
// Xóa lỗi cũ
|
|
this.errors = this.errors.filter((err) => err.name !== 'phone');
|
|
|
|
const res = this.$validatePhone(phone, true);
|
|
|
|
if (!res.status) {
|
|
this.errors.push({
|
|
name: 'phone',
|
|
text: res.message,
|
|
});
|
|
}
|
|
},
|
|
|
|
async createAccount() {
|
|
if (this.checkError()) return;
|
|
const userRes = await this.$getdata('user', { email: this.email });
|
|
const customerRes = await this.$getdata('customer', { email: this.email });
|
|
|
|
if (userRes.length > 0 || customerRes.length > 0) {
|
|
return this.errors.push({ name: 'email', text: 'Tài khoản đã tồn tại trong hệ thống.' });
|
|
}
|
|
|
|
let rs = await this.$insertapi('gethash', { text: this.password });
|
|
this.hash = rs.rows[0];
|
|
let ele = this.$findapi('user');
|
|
ele.params = { filter: { email: this.email } };
|
|
let result = await this.$getapi([ele]);
|
|
let data = result.find((v) => v.name === 'user').data.rows;
|
|
if (data.length > 0) {
|
|
return this.errors.push({ name: 'email', text: 'Tài khoản đã tồn tại trong hệ thống.' });
|
|
}
|
|
|
|
let registerMethod = this.registermethod.find((v) => v.code === 'create-account').id;
|
|
if (this.type) {
|
|
if (this.authEmail !== this.email) this.type = undefined;
|
|
else registerMethod = this.registermethod.find((v) => v.code === this.type).id;
|
|
}
|
|
this.status = this.authstatus.find((v) => v.code === 'auth');
|
|
if (!this.type && this.email.indexOf('@') >= 0) this.status = this.authstatus.find((v) => v.code === 'wait');
|
|
data = {
|
|
fullname: this.fullname,
|
|
email: this.email,
|
|
password: this.hash,
|
|
type: this.usertype.find((v) => v.code === 'customer').id,
|
|
register_method: registerMethod,
|
|
auth_status: this.status.id,
|
|
phone: this.phone,
|
|
auth_method:
|
|
this.email.indexOf('@') >= 0
|
|
? this.authmethod.find((v) => v.code === 'email').id
|
|
: this.authmethod.find((v) => v.code === 'phone').id,
|
|
display_name: this.fullname,
|
|
};
|
|
this.$route.query.type === 'employee' ? (data.type = this.usertype.find((v) => v.code === 'employee').id) : false;
|
|
if (this.referer) data.referer = this.referer;
|
|
|
|
this.user = await this.$insertapi('user', data);
|
|
const customer = await this.$insertapi('customer', { ...data, type: null });
|
|
if (this.user !== 'error' && customer != 'error') {
|
|
let text = 'Bạn đã đăng ký tài khoản thành công. Hệ thống tự động chuyển tới trang đăng nhập.';
|
|
if (this.status.code === 'wait')
|
|
text = 'Bạn đã đăng ký tài khoản thành công. Hệ thống tự động chuyển tới trang xác thực tài khoản';
|
|
this.$dialog(text, 'Xác thực tài khoản', undefined, 5);
|
|
}
|
|
if (this.status.code === 'wait') {
|
|
this.code = this.$id();
|
|
let data = { user: this.user.id, code: this.code };
|
|
result = await this.$insertapi('userauth', data);
|
|
this.processAuth();
|
|
} else this.$router.push({ path: '/signin' });
|
|
},
|
|
async processAuth() {
|
|
let query = this.$store.state.link ? { code: this.code, link: this.$store.state.link } : { code: this.code };
|
|
let routeData = this.$router.resolve({ path: '/account/auth', query: query });
|
|
let path = window.location.origin + routeData.href;
|
|
let conn = this.$findapi('userauth');
|
|
conn.params.filter = { user: this.user.id };
|
|
let result = await this.$getapi([conn]);
|
|
|
|
let msg = result[0].data.rows[0].code;
|
|
msg = msg.replace('[1]', this.fullname);
|
|
msg = msg.replace('[2]', this.code);
|
|
msg = msg.replace('[3]', path);
|
|
|
|
let data = { subject: 'Xác thực tài khoản BigDataTechCloud', content: msg, to: this.user.email };
|
|
result = await this.$insertapi('sendemail', data);
|
|
this.$router.push({ path: '/account/auth', query: { id: this.user.id, email: this.email } });
|
|
},
|
|
onSuccess(googleUser) {
|
|
let info = googleUser.getBasicProfile();
|
|
let keys = Object.keys(info);
|
|
this.email = info[keys[5]];
|
|
this.fullname = info[keys[1]];
|
|
this.type = 'google';
|
|
this.authEmail = this.$copy(this.email);
|
|
},
|
|
async logInWithFacebook() {
|
|
if (window.FB) {
|
|
let self = this;
|
|
window.FB.login(
|
|
function (response) {
|
|
if (response.authResponse) {
|
|
let userId = response.authResponse.userID;
|
|
self.getUserInfo(userId);
|
|
}
|
|
},
|
|
{
|
|
scope: 'public_profile,email',
|
|
return_scopes: true,
|
|
auth_type: 'rerequest',
|
|
},
|
|
);
|
|
} else {
|
|
await this.loadFacebookSDK(document, 'script', 'facebook-jssdk');
|
|
await this.initFacebook();
|
|
}
|
|
},
|
|
async initFacebook() {
|
|
let self = this;
|
|
window.fbAsyncInit = function () {
|
|
window.FB.init({
|
|
appId: '434259677606053',
|
|
cookie: true,
|
|
version: 'v8.0',
|
|
});
|
|
self.isLoaded = true;
|
|
};
|
|
},
|
|
async loadFacebookSDK(d, s, id) {
|
|
var js,
|
|
fjs = d.getElementsByTagName(s)[0];
|
|
if (d.getElementById(id)) {
|
|
return;
|
|
}
|
|
js = d.createElement(s);
|
|
js.id = id;
|
|
js.src = 'https://connect.facebook.net/en_US/sdk.js';
|
|
fjs.parentNode.insertBefore(js, fjs);
|
|
},
|
|
getUserInfo(userId) {
|
|
let self = this;
|
|
window.FB.api('/me?fields=id,name,email', function (response) {
|
|
self.username = response.email;
|
|
self.fullname = response.name;
|
|
self.type = 'facebook';
|
|
self.authEmail = self.$copy(self.username);
|
|
});
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
.login-page {
|
|
background-color: #f0fdf4;
|
|
min-height: 100vh;
|
|
align-content: center;
|
|
|
|
button.is-primary {
|
|
background-color: #53b147;
|
|
box-shadow: 0 4px 12px rgba(83, 177, 71, 0.25);
|
|
&:hover {
|
|
background: #45963a;
|
|
transform: translateY(-1px);
|
|
}
|
|
}
|
|
|
|
.wrapper-login {
|
|
border-radius: 20px;
|
|
overflow: hidden;
|
|
border: 1px solid #bbf7d0;
|
|
background-color: #fff;
|
|
padding: 0;
|
|
// .login-header {
|
|
// }
|
|
.login-body {
|
|
.form-login {
|
|
.group-action {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
}
|
|
}
|
|
.social-login {
|
|
.social-header {
|
|
text-align: center;
|
|
position: relative;
|
|
&::after {
|
|
content: '';
|
|
position: absolute;
|
|
left: 0;
|
|
top: 50%;
|
|
width: 100%;
|
|
height: 1px;
|
|
background-color: #ddd;
|
|
}
|
|
span {
|
|
position: relative;
|
|
z-index: 1;
|
|
background-color: #fff;
|
|
padding: 0 10px;
|
|
}
|
|
}
|
|
.social-body {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
|
|
.social-item {
|
|
flex: 1;
|
|
button {
|
|
height: fit-content;
|
|
width: 100%;
|
|
font-weight: 600;
|
|
&:hover {
|
|
background-color: #bbf7d0;
|
|
border-color: #bbf7d0;
|
|
}
|
|
}
|
|
.icon {
|
|
width: 24px;
|
|
height: 24px;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.info-support {
|
|
background-color: #fff;
|
|
border: 1px solid #bbf7d0;
|
|
border-radius: 16px;
|
|
overflow: hidden;
|
|
|
|
.support-header {
|
|
background-color: #f0fdf4;
|
|
text-align: center;
|
|
padding: 14px 0;
|
|
.title {
|
|
font-weight: bold;
|
|
font-size: 16px;
|
|
}
|
|
}
|
|
.support-body {
|
|
display: flex;
|
|
padding: 16px 0;
|
|
align-items: center;
|
|
justify-content: space-around;
|
|
.support-item {
|
|
a {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 6px;
|
|
color: #374151;
|
|
&:hover {
|
|
color: #16a34a;
|
|
img {
|
|
filter: brightness(0) saturate(100%) invert(40%) sepia(80%) saturate(400%) hue-rotate(90deg);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.login-footer {
|
|
margin-top: 24px;
|
|
padding-top: 12px;
|
|
border-top: 1px solid #e5e7eb;
|
|
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
|
|
font-size: 12px;
|
|
color: #9ca3af;
|
|
|
|
span {
|
|
white-space: nowrap;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</style>
|