Change update UI Signin

This commit is contained in:
Thien Pham Van
2026-01-28 14:55:28 +07:00
parent 576a50c113
commit 4abf0af565
2 changed files with 253 additions and 73 deletions

View File

@@ -1,7 +1,19 @@
<template> <template>
<div> <div class="logo">
<a @click="$router.push('/signin')"> <a class="link" @click="$router.push('/signin')">
<img width="90px" src="/logo.png" /> <img width="90px" src="/logo.png" />
</a> </a>
</div> </div>
</template> </template>
<style>
.logo,
.logo .link {
display: flex;
justify-content: center;
align-items: center;
}
.logo .link {
text-decoration: none;
}
</style>

View File

@@ -1,16 +1,19 @@
<template> <template>
<div class="columns is-centered px-0 mx-0"> <div class="columns is-centered px-0 mx-0">
<div <div
:class="`column is-flex is-justify-content-center is-align-items-center wrapper-login is-${ :class="`column wrapper-login is-${
viewport >= 4 ? 4 : 6 viewport >= 4 ? 4 : 6
}`" }`"
> >
<div class="has-background-white px-6 py-6 login-box "> <div class="has-background-white px-6 py-6 login-box ">
<div class="login-header">
<Logo /> <Logo />
<Caption <h2 class="title mt-4">{{ isVietnamese ? 'Đăng nhập hệ thống' : 'Login' }}</h2>
:class="`mt-5 pt-${viewport === 1 ? 0 : 3}`" <p class="subtitle is-6 has-text-centered" v-if="getEnv">
v-bind="{ title: `${isVietnamese ? 'Đăng nhập' : 'Login'}`, size: 19 }" {{ getEnv }}
></Caption> </p>
</div>
<div class="login-body mt-3">
<div class="field mt-5"> <div class="field mt-5">
<label class="label" <label class="label"
>{{ isVietnamese ? 'Tên đăng nhập' : 'User name' }}<b class="ml-1 has-text-danger">*</b></label >{{ isVietnamese ? 'Tên đăng nhập' : 'User name' }}<b class="ml-1 has-text-danger">*</b></label
@@ -31,7 +34,9 @@
></p> ></p>
</div> </div>
<div class="field mt-5"> <div class="field mt-5">
<label class="label">{{ isVietnamese ? 'Mật khẩu' : 'Password' }}<b class="ml-1 has-text-danger">*</b></label> <label class="label"
>{{ isVietnamese ? 'Mật khẩu' : 'Password' }}<b class="ml-1 has-text-danger">*</b></label
>
<div class="field-body"> <div class="field-body">
<div class="field has-addons"> <div class="field has-addons">
<p class="control is-expanded"> <p class="control is-expanded">
@@ -45,10 +50,16 @@
</p> </p>
<div class="control"> <div class="control">
<a <a
class="button" class="button view-pass-btn"
@click="showpass = !showpass" @click="showpass = !showpass"
:title=" :title="
showpass ? (isVietnamese ? 'Đăng nhập' : 'Login') : isVietnamese ? 'Hiện mật khẩu' : 'Show Password' showpass
? isVietnamese
? 'Đăng nhập'
: 'Login'
: isVietnamese
? 'Hiện mật khẩu'
: 'Show Password'
" "
> >
<span class="icon"> <span class="icon">
@@ -62,19 +73,23 @@
{{ errors.find((v) => v.name === 'password').text }} {{ errors.find((v) => v.name === 'password').text }}
</p> </p>
</div> </div>
<div class="field is-grouped mt-5 is-align-items-center"> <div class="field is-grouped mt-5 is-align-items-center is-justify-content-space-between">
<div class="control is-expanded"> <div class="control ">
<button class="button is-primary" @click="signin()" @keyup.enter="signin()"> <button class="button btn-primary" @click="signin()" @keyup.enter="signin()">
{{ isVietnamese ? 'Đăng nhập' : 'Login' }} {{ isVietnamese ? 'Đăng nhập' : 'Login' }}
</button> </button>
</div> </div>
<div class="control"> <div class="control reset-wrapper">
<a class="is-primary" @click="accountRecovery()" <a class="reset-link" @click="accountRecovery()"
>{{ isVietnamese ? 'Đặt lại mật khẩu' : 'Forgot Password' }}?</a >{{ isVietnamese ? 'Đặt lại mật khẩu' : 'Forgot Password' }}?</a
> >
</div> </div>
</div> </div>
</div> </div>
<footer class="login-footer">
<span>© 2026 {{ isVietnamese ? 'Bản quyền thuộc về Utopia' : 'Copyright by Utopia Technology JSC' }}</span>
</footer>
</div>
</div> </div>
</div> </div>
</template> </template>
@@ -166,6 +181,42 @@ export default {
isVietnamese() { isVietnamese() {
return navigator.language.toLowerCase().startsWith('vi'); return navigator.language.toLowerCase().startsWith('vi');
}, },
getEnv() {
const link = (this.$route.query.link || '').replace(/^https?:\/\//, '').toLowerCase();
const module = (this.$route.query.module || '').replace(/^https?:\/\//, '').toLowerCase();
const isDev = link.includes('dev');
const ENV_MAP = [
{
key: 'system',
module: 'system',
vi: 'Cổng quản trị hệ thống',
en: 'Admin Portal',
},
{
key: 'biz',
module: 'application',
vi: 'Cổng thông tin Chủ đầu tư',
en: 'Business Portal',
},
{
key: 'dealer',
module: 'dealer',
vi: 'Cổng thông tin Đại lý',
en: 'Dealer Portal',
},
];
const found = ENV_MAP.find((item) => link.includes(item.key) && module === item.module);
if (found) {
const title = this.isVietnamese ? found.vi : found.en;
return isDev
? `${title} - ${this.isVietnamese ? 'Môi trường phát triển (DEV)' : 'Development Environment (DEV)'}`
: title;
} else {
return null;
}
},
}, },
methods: { methods: {
checkError() { checkError() {
@@ -176,13 +227,13 @@ export default {
if (this.$empty(this.username)) { if (this.$empty(this.username)) {
this.errors.push({ this.errors.push({
name: 'username', name: 'username',
text: this.isVietnamese ? 'Tên đăng nhập không được bỏ trống.' : 'Username is required.', text: this.isVietnamese ? 'Vui lòng nhập tên đăng nhập.' : 'Please enter your username.',
}); });
} }
if (this.$empty(this.password)) { if (this.$empty(this.password)) {
this.errors.push({ this.errors.push({
name: 'password', name: 'password',
text: this.isVietnamese ? 'Mật khẩu không được bỏ trống.' : 'Password is required.', text: this.isVietnamese ? 'Vui lòng nhập mật khẩu.' : 'Please enter your password.',
}); });
} else if (this.password.length < 6) { } else if (this.password.length < 6) {
this.errors.push({ this.errors.push({
@@ -224,7 +275,7 @@ export default {
}, },
invalidLogin(data) { invalidLogin(data) {
if (!data) { if (!data) {
const text = this.isVietnamese ? 'Tài khoản hoặc mật khẩu không chính xác' : 'Invalid username or password.'; const text = this.isVietnamese ? 'Tên đăng nhập hoặc mật khẩu không đúng.' : 'Incorrect username or password.';
this.errors.push({ name: 'username', text: text }); this.errors.push({ name: 'username', text: text });
this.errors.push({ name: 'password', text: text }); this.errors.push({ name: 'password', text: text });
} else if (data.blocked) { } else if (data.blocked) {
@@ -266,7 +317,7 @@ export default {
await this.$insertapi('authtoken', obj); await this.$insertapi('authtoken', obj);
let link = this.$store.state.link; let link = this.$store.state.link;
if (data.type === 3 && link.indexOf('y99') >= 0) { if (data.type === 3 && link.indexOf('y99') >= 0) {
link = link.indexOf('dev') >= 0 ? 'https://dev.ctv.y99.vn' : 'https://ctv.y99.vn'; link = link.indexOf('dev') >= 0 ? '' : '';
} }
let href = `${link}?username=${ele.username}&token=${ele.token}&fullname=${ele.fullname}&userid=${ele.id}`; let href = `${link}?username=${ele.username}&token=${ele.token}&fullname=${ele.fullname}&userid=${ele.id}`;
if (ele.avatar) href = `${href}&avatar=${ele.avatar}`; if (ele.avatar) href = `${href}&avatar=${ele.avatar}`;
@@ -299,12 +350,129 @@ export default {
</script> </script>
<style> <style>
.wrapper-login { .wrapper-login {
min-height: 100vh; height: 100vh;
overflow: hidden;
} }
.wrapper-login .login-box { .wrapper-login .login-box {
margin-top: 20%;
width: 100%; width: 100%;
border-radius: 16px; border-radius: 16px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
} }
.wrapper-login .login-box .login-header .title {
text-align: center;
color: #c66a1c;
font-size: 28px;
font-weight: 600;
line-height: 36px;
letter-spacing: -0.2px;
}
.subtitle {
font-size: 15px;
color: #3F6E73;
margin-bottom: 24px;
}
.wrapper-login .login-box .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;
}
.wrapper-login .login-box .login-footer span {
white-space: nowrap;
}
.wrapper-login .btn-primary {
width: 100%;
height: 46px;
background-color: #1F3A3D; /* primary from logo */
color: #FFFFFF;
border: none;
border-radius: 12px;
font-size: 15px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s ease;
}
.wrapper-login .btn-primary:hover {
background-color: #2C5054;
}
.wrapper-login .btn-primary:active {
transform: translateY(1px);
}
.wrapper-login .btn-primary:disabled {
background-color: #9CA3AF;
cursor: not-allowed;
}
.wrapper-login .reset-wrapper {
text-align: right;
}
.wrapper-login .reset-link {
font-size: 14px;
font-weight: 500;
color: #3F6E73;
text-decoration: none;
transition: all 0.2s ease;
}
.wrapper-login .reset-link:hover {
color: #1F3A3D;
text-decoration: underline;
}
/* INPUT BASE */
.wrapper-login input {
width: 100%;
height: 44px;
padding: 10px 14px;
border: 1px solid #e5e7eb;
border-radius: 10px;
font-size: 14px;
transition: all 0.2s ease;
outline: none;
}
/* PLACEHOLDER */
.wrapper-login input::placeholder {
color: #9ca3af;
}
/* FOCUS STATE */
.wrapper-login input:focus {
border-color: #1F3A3D;
box-shadow: 0 0 0 3px rgba(31, 58, 61, 0.12);
}
/* HOVER nhẹ */
.wrapper-login input:hover {
border-color: #cbd5e1;
}
.wrapper-login .view-pass-btn {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
</style> </style>