1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-02-08 01:20:07 +08:00

feat: Update Login Page (#7815)

This commit is contained in:
zhengkunwang 2025-02-07 18:15:20 +08:00 committed by GitHub
parent b2fa0aa2d4
commit 88684c7cf0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 284 additions and 227 deletions

View File

@ -2,7 +2,6 @@ package v2
import (
"encoding/base64"
"github.com/1Panel-dev/1Panel/core/app/api/v2/helper"
"github.com/1Panel-dev/1Panel/core/app/dto"
"github.com/1Panel-dev/1Panel/core/app/model"
@ -28,8 +27,8 @@ func (b *BaseApi) Login(c *gin.Context) {
}
if req.AuthMethod != "jwt" && !req.IgnoreCaptcha {
if err := captcha.VerifyCode(req.CaptchaID, req.Captcha); err != nil {
helper.InternalServer(c, err)
if errMsg := captcha.VerifyCode(req.CaptchaID, req.Captcha); errMsg != "" {
helper.BadAuth(c, errMsg, nil)
return
}
}
@ -39,8 +38,12 @@ func (b *BaseApi) Login(c *gin.Context) {
entrance, _ = base64.StdEncoding.DecodeString(entranceItem)
}
user, err := authService.Login(c, req, string(entrance))
user, msgKey, err := authService.Login(c, req, string(entrance))
go saveLoginLogs(c, err)
if msgKey == "ErrAuth" {
helper.BadAuth(c, msgKey, err)
return
}
if err != nil {
helper.InternalServer(c, err)
return
@ -67,7 +70,11 @@ func (b *BaseApi) MFALogin(c *gin.Context) {
entrance, _ = base64.StdEncoding.DecodeString(entranceItem)
}
user, err := authService.MFALogin(c, req, string(entrance))
user, msgKey, err := authService.MFALogin(c, req, string(entrance))
if msgKey == "ErrAuth" {
helper.BadAuth(c, msgKey, err)
return
}
if err != nil {
helper.InternalServer(c, err)
return
@ -141,16 +148,7 @@ func saveLoginLogs(c *gin.Context, err error) {
logs.Status = constant.StatusSuccess
}
logs.IP = c.ClientIP()
//lang := c.GetHeader("Accept-Language")
//if lang == "" {
// lang = "zh"
//}
//address, err := geo.GetIPLocation(logs.IP, lang)
//if err != nil {
// global.LOG.Errorf("get ip location failed: %s", err)
//}
logs.Agent = c.GetHeader("User-Agent")
//logs.Address = address
_ = logService.CreateLoginLog(logs)
}

View File

@ -15,7 +15,7 @@ func ErrorWithDetail(ctx *gin.Context, code int, msgKey string, err error) {
Message: "",
}
if msgKey == "ErrCaptchaCode" || msgKey == "ErrAuth" {
res.Code = 406
res.Code = 401
res.Message = msgKey
}
res.Message = i18n.GetMsgWithMap(msgKey, map[string]interface{}{"detail": err})

View File

@ -13,6 +13,7 @@ type ISettingRepo interface {
GetValueByKey(key string) (string, error)
Create(key, value string) error
Update(key, value string) error
UpdateOrCreate(key, value string) error
}
func NewISettingRepo() ISettingRepo {
@ -58,3 +59,7 @@ func (u *SettingRepo) GetValueByKey(key string) (string, error) {
func (u *SettingRepo) Update(key, value string) error {
return global.DB.Model(&model.Setting{}).Where("key = ?", key).Updates(map[string]interface{}{"value": value}).Error
}
func (u *SettingRepo) UpdateOrCreate(key, value string) error {
return global.DB.Model(&model.Setting{}).Where("key = ?", key).Assign(model.Setting{Key: key, Value: value}).FirstOrCreate(&model.Setting{}).Error
}

View File

@ -21,9 +21,9 @@ type IAuthService interface {
CheckIsSafety(code string) (string, error)
GetResponsePage() (string, error)
VerifyCode(code string) (bool, error)
Login(c *gin.Context, info dto.Login, entrance string) (*dto.UserLoginInfo, error)
Login(c *gin.Context, info dto.Login, entrance string) (*dto.UserLoginInfo, string, error)
LogOut(c *gin.Context) error
MFALogin(c *gin.Context, info dto.MFALogin, entrance string) (*dto.UserLoginInfo, error)
MFALogin(c *gin.Context, info dto.MFALogin, entrance string) (*dto.UserLoginInfo, string, error)
GetSecurityEntrance() string
IsLogin(c *gin.Context) bool
}
@ -32,79 +32,86 @@ func NewIAuthService() IAuthService {
return &AuthService{}
}
func (u *AuthService) Login(c *gin.Context, info dto.Login, entrance string) (*dto.UserLoginInfo, error) {
func (u *AuthService) Login(c *gin.Context, info dto.Login, entrance string) (*dto.UserLoginInfo, string, error) {
nameSetting, err := settingRepo.Get(repo.WithByKey("UserName"))
if err != nil {
return nil, buserr.New("ErrRecordNotFound")
return nil, "", buserr.New("ErrRecordNotFound")
}
passwordSetting, err := settingRepo.Get(repo.WithByKey("Password"))
if err != nil {
return nil, buserr.New("ErrRecordNotFound")
return nil, "", buserr.New("ErrRecordNotFound")
}
pass, err := encrypt.StringDecrypt(passwordSetting.Value)
if err != nil {
return nil, buserr.New("ErrAuth")
return nil, "ErrAuth", nil
}
if !hmac.Equal([]byte(info.Password), []byte(pass)) || nameSetting.Value != info.Name {
return nil, buserr.New("ErrAuth")
return nil, "ErrAuth", nil
}
entranceSetting, err := settingRepo.Get(repo.WithByKey("SecurityEntrance"))
if err != nil {
return nil, err
return nil, "", err
}
if len(entranceSetting.Value) != 0 && entranceSetting.Value != entrance {
return nil, buserr.New("ErrEntrance")
return nil, "ErrEntrance", nil
}
mfa, err := settingRepo.Get(repo.WithByKey("MFAStatus"))
if err != nil {
return nil, err
return nil, "", err
}
if err = settingRepo.Update("Language", info.Language); err != nil {
return nil, err
return nil, "", err
}
if mfa.Value == constant.StatusEnable {
return &dto.UserLoginInfo{Name: nameSetting.Value, MfaStatus: mfa.Value}, nil
return &dto.UserLoginInfo{Name: nameSetting.Value, MfaStatus: mfa.Value}, "", nil
}
return u.generateSession(c, info.Name, info.AuthMethod)
res, err := u.generateSession(c, info.Name, info.AuthMethod)
if err != nil {
return nil, "", err
}
return res, "", nil
}
func (u *AuthService) MFALogin(c *gin.Context, info dto.MFALogin, entrance string) (*dto.UserLoginInfo, error) {
func (u *AuthService) MFALogin(c *gin.Context, info dto.MFALogin, entrance string) (*dto.UserLoginInfo, string, error) {
nameSetting, err := settingRepo.Get(repo.WithByKey("UserName"))
if err != nil {
return nil, buserr.New("ErrRecordNotFound")
return nil, "", buserr.New("ErrRecordNotFound")
}
passwordSetting, err := settingRepo.Get(repo.WithByKey("Password"))
if err != nil {
return nil, buserr.New("ErrRecordNotFound")
return nil, "", buserr.New("ErrRecordNotFound")
}
pass, err := encrypt.StringDecrypt(passwordSetting.Value)
if err != nil {
return nil, err
return nil, "", err
}
if !hmac.Equal([]byte(info.Password), []byte(pass)) || nameSetting.Value != info.Name {
return nil, buserr.New("ErrAuth")
return nil, "ErrAuth", nil
}
entranceSetting, err := settingRepo.Get(repo.WithByKey("SecurityEntrance"))
if err != nil {
return nil, err
return nil, "", err
}
if len(entranceSetting.Value) != 0 && entranceSetting.Value != entrance {
return nil, buserr.New("ErrEntrance")
return nil, "", buserr.New("ErrEntrance")
}
mfaSecret, err := settingRepo.Get(repo.WithByKey("MFASecret"))
if err != nil {
return nil, err
return nil, "", err
}
mfaInterval, err := settingRepo.Get(repo.WithByKey("MFAInterval"))
if err != nil {
return nil, err
return nil, "", err
}
success := mfa.ValidCode(info.Code, mfaInterval.Value, mfaSecret.Value)
if !success {
return nil, buserr.New("ErrAuth")
return nil, "ErrAuth", nil
}
return u.generateSession(c, info.Name, info.AuthMethod)
res, err := u.generateSession(c, info.Name, info.AuthMethod)
if err != nil {
return nil, "", err
}
return res, "", nil
}
func (u *AuthService) generateSession(c *gin.Context, name, authMethod string) (*dto.UserLoginInfo, error) {

View File

@ -4,24 +4,23 @@ import (
"strings"
"github.com/1Panel-dev/1Panel/core/app/dto"
"github.com/1Panel-dev/1Panel/core/buserr"
"github.com/mojocn/base64Captcha"
)
var store = base64Captcha.DefaultMemStore
func VerifyCode(codeID string, code string) error {
func VerifyCode(codeID string, code string) string {
if codeID == "" {
return buserr.New("ErrCaptchaCode")
return "ErrCaptchaCode"
}
vv := store.Get(codeID, true)
vv = strings.TrimSpace(vv)
code = strings.TrimSpace(code)
if strings.EqualFold(vv, code) {
return nil
return ""
}
return buserr.New("ErrCaptchaCode")
return "ErrCaptchaCode"
}
func CreateCaptcha() (*dto.CaptchaResponse, error) {

View File

@ -1,5 +1,5 @@
<template>
<el-drawer v-model="localOpenPage" :destroy-on-close="true" :size="size">
<el-drawer v-model="localOpenPage" :destroy-on-close="true" :size="size" :close-on-press-escape="true">
<template #header>
<el-page-header @back="handleBack">
<template #content>

View File

@ -177,8 +177,7 @@ const message = {
mfaTitle: 'MFA Certification',
mfaCode: 'MFA verification code',
title: 'Linux Server Management Panel',
licenseHelper:
'Agree &laquo; <a href="https://www.fit2cloud.com/legal/licenses.html" target="_blank">Community License Agreement</a> &raquo;',
licenseHelper: '<Community License Agreement>',
errorAgree: 'Click to agree to the Community Software License',
logout: 'Logout',
agreeTitle: 'Agreement',

View File

@ -167,8 +167,7 @@ const message = {
mfaTitle: 'MFA認定',
mfaCode: 'MFA検証コード',
title: 'Linuxサーバー管理パネル',
licenseHelper:
'同意laquo;<a href = "https://www.fit2cloud.com/legal/licenses.html" target="_blank">コミュニティライセンス契約</a>raquo;',
licenseHelper: '<コミュニティライセンス契約>',
errorAgree: 'クリックしてコミュニティソフトウェアライセンスに同意します',
logout: 'ログアウト',
agreeTitle: '合意',

View File

@ -166,8 +166,7 @@ const message = {
mfaTitle: 'MFA 인증',
mfaCode: 'MFA 인증 코드',
title: 'Linux 서버 관리 패널',
licenseHelper:
'&laquo; <a href="https://www.fit2cloud.com/legal/licenses.html" target="_blank">커뮤니티 라이선스 계약</a> &raquo; 동의합니다',
licenseHelper: '<커뮤니티 라이선스 계약>',
errorAgree: '커뮤니티 소프트웨어 라이선스에 동의하려면 클릭하세요',
logout: '로그아웃',
agreeTitle: '동의',

View File

@ -169,8 +169,7 @@ const message = {
mfaTitle: 'Pengesahan MFA',
mfaCode: 'Kod pengesahan MFA',
title: 'Panel Pengurusan Pelayan Linux',
licenseHelper:
'Setuju &laquo; <a href="https://www.fit2cloud.com/legal/licenses.html" target="_blank">Perjanjian Lesen Komuniti</a> &raquo;',
licenseHelper: '<Perjanjian Lesen Komuniti>',
errorAgree: 'Klik untuk bersetuju dengan Lesen Perisian Komuniti',
logout: 'Log keluar',
agreeTitle: 'Agreement',

View File

@ -168,8 +168,7 @@ const message = {
mfaTitle: 'Autenticação MFA',
mfaCode: 'Código de verificação MFA',
title: 'Painel de Gerenciamento de Servidores Linux',
licenseHelper:
'Concordar com &laquo; <a href="https://www.fit2cloud.com/legal/licenses.html" target="_blank">Acordo de Licença Comunitária</a> &raquo;',
licenseHelper: '<Acordo de Licença Comunitária>',
errorAgree: 'Clique para concordar com o Acordo de Licença de Software Comunitário',
logout: 'Sair',
agreeTitle: 'Termo de Aceite',

View File

@ -167,8 +167,7 @@ const message = {
mfaTitle: 'MFA Сертификация',
mfaCode: 'MFA код подтверждения',
title: 'Панель управления Linux сервером',
licenseHelper:
'Согласен &laquo; <a href="https://www.fit2cloud.com/legal/licenses.html" target="_blank">Лицензионное соглашение сообщества</a> &raquo;',
licenseHelper: '<Лицензионное соглашение сообщества>',
errorAgree: 'Нажмите, чтобы согласиться с Лицензией программного обеспечения сообщества',
logout: 'Выход',
agreeTitle: 'Соглашение',

View File

@ -175,8 +175,7 @@ const message = {
mfaTitle: 'MFA 認證',
mfaCode: 'MFA 驗證碼',
title: 'Linux 服務器運維管理面板',
licenseHelper:
'同意 &laquo; <a href="https://www.fit2cloud.com/legal/licenses.html" target="_blank"> 飛致雲社區軟件許可協議</a> &raquo;',
licenseHelper: '飛致雲社區軟件許可協議',
errorAgree: '請點擊同意社區軟件許可協議',
agreeTitle: '服務協議及隱私保護',
agreeContent:

View File

@ -175,8 +175,7 @@ const message = {
mfaTitle: 'MFA 认证',
mfaCode: 'MFA 验证码',
title: 'Linux 服务器运维管理面板',
licenseHelper:
'同意 &laquo; <a href="https://www.fit2cloud.com/legal/licenses.html" target="_blank"> 飞致云社区软件许可协议</a> &raquo;',
licenseHelper: '飞致云社区软件许可协议',
errorAgree: '请点击同意社区软件许可协议',
agreeTitle: '服务协议及隐私保护',
agreeContent:

View File

@ -386,7 +386,7 @@ import AppIgnore from './ignore/index.vue';
import ComposeLogs from '@/components/compose-log/index.vue';
import { App } from '@/api/interface/app';
import Status from '@/components/status/index.vue';
import { getAge, getLanguage } from '@/utils/util';
import { getAge } from '@/utils/util';
import { useRouter } from 'vue-router';
import { MsgSuccess } from '@/utils/message';
import { toFolder } from '@/global/business';
@ -432,7 +432,6 @@ const router = useRouter();
const activeName = ref(i18n.global.t('app.installed'));
const mode = ref('installed');
const moreTag = ref('');
const language = getLanguage();
const defaultLink = ref('');
const options = {

View File

@ -185,13 +185,11 @@ import { changeLauncherStatus, loadAppLauncher, loadAppLauncherOption } from '@/
import i18n from '@/lang';
import { GlobalStore } from '@/store';
import { MsgSuccess } from '@/utils/message';
import { getLanguage } from '@/utils/util';
import { ref } from 'vue';
import { useRouter } from 'vue-router';
import { toFolder } from '@/global/business';
const router = useRouter();
const language = getLanguage();
const globalStore = GlobalStore();
let loading = ref(false);

View File

@ -1,50 +1,49 @@
<template>
<div v-loading="loading">
<div v-if="mfaShow">
<div class="login-form">
<div class="w-full h-full flex items-center justify-center px-8 py-6">
<div v-loading="loading" class="w-full flex-grow flex flex-col">
<div v-if="mfaShow">
<el-form @submit.prevent>
<div class="login-title">{{ $t('commons.login.mfaTitle') }}</div>
<el-form-item class="no-border">
<el-input
size="default"
:placeholder="$t('commons.login.mfaCode')"
v-model.trim="mfaLoginForm.code"
@input="mfaLogin(true)"
>
<template #prefix>
<el-icon class="el-input__icon">
<Finished />
</el-icon>
</template>
</el-input>
<span v-if="errMfaInfo" class="input-error" style="line-height: 14px">
{{ $t('commons.login.errorMfaInfo') }}
</span>
</el-form-item>
<el-form-item>
<el-button
@focus="mfaButtonFocused = true"
@blur="mfaButtonFocused = false"
class="login-button"
type="primary"
size="default"
round
@click="mfaLogin(false)"
>
{{ $t('commons.button.verify') }}
</el-button>
</el-form-item>
<div class="flex flex-col justify-center items-center mb-6">
<div class="text-2xl font-medium text-gray-900 text-center">
{{ $t('commons.login.mfaTitle') }}
</div>
</div>
<div class="space-y-6 flex-grow">
<el-form-item>
<el-input
size="large"
:placeholder="$t('commons.login.mfaCode')"
v-model.trim="mfaLoginForm.code"
@input="mfaLogin(true)"
></el-input>
<div class="h-1">
<span v-if="errMfaInfo" class="input-error">
{{ $t('commons.login.errorMfaInfo') }}
</span>
</div>
</el-form-item>
<el-form-item>
<el-button
@focus="mfaButtonFocused = true"
@blur="mfaButtonFocused = false"
class="w-full"
type="primary"
@click="mfaLogin(false)"
>
{{ $t('commons.button.verify') }}
</el-button>
</el-form-item>
</div>
</el-form>
</div>
</div>
<div v-else>
<div class="login-form">
<div v-else>
<el-form ref="loginFormRef" :model="loginForm" size="default" :rules="loginRules">
<div class="login-form-header">
<div class="title cursor-pointer">{{ $t('commons.button.login') }}</div>
<div class="flex justify-between items-center mb-6">
<div class="text-2xl font-medium text-gray-900">{{ $t('commons.button.login') }}</div>
<div class="cursor-pointer">
<el-dropdown @command="handleCommand">
<span>
<span class="flex items-center space-x-1">
{{ dropdownText }}
<el-icon>
<arrow-down />
@ -70,75 +69,85 @@
</el-dropdown>
</div>
</div>
<el-form-item prop="name" class="no-border">
<el-input
v-model.trim="loginForm.name"
:placeholder="$t('commons.login.username')"
class="form-input"
>
<template #prefix>
<el-icon class="el-input__icon">
<user />
</el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item prop="password" class="no-border">
<el-input
type="password"
clearable
v-model.trim="loginForm.password"
show-password
:placeholder="$t('commons.login.password')"
>
<template #prefix>
<el-icon class="el-input__icon">
<lock />
</el-icon>
</template>
</el-input>
<span v-if="errAuthInfo" class="input-error" style="line-height: 14px">
{{ $t('commons.login.errorAuthInfo') }}
</span>
</el-form-item>
<el-form-item v-if="!globalStore.ignoreCaptcha" prop="captcha" class="login-captcha">
<el-input v-model.trim="loginForm.captcha" :placeholder="$t('commons.login.captchaHelper')">
<template #prefix>
<svg-icon style="font-size: 7px" iconName="p-yanzhengma1"></svg-icon>
</template>
</el-input>
<img
v-if="captcha.imagePath"
:src="captcha.imagePath"
:alt="$t('commons.login.captchaHelper')"
@click="loginVerify()"
/>
<span v-if="errCaptcha" class="input-error" style="line-height: 14px">
{{ $t('commons.login.errorCaptcha') }}
</span>
</el-form-item>
<el-form-item>
<el-button
@click="login(loginFormRef)"
@focus="loginButtonFocused = true"
@blur="loginButtonFocused = false"
class="login-button"
type="primary"
size="default"
round
>
{{ $t('commons.button.login') }}
</el-button>
</el-form-item>
<template v-if="!isIntl">
<el-form-item prop="agreeLicense">
<el-checkbox v-model="loginForm.agreeLicense">
<template #default>
<span class="agree" v-html="$t('commons.login.licenseHelper')"></span>
</template>
</el-checkbox>
<div class="space-y-6 flex-grow">
<el-form-item prop="name" class="w-full">
<el-input
v-model.trim="loginForm.name"
:placeholder="$t('commons.login.username')"
class="w-full"
size="large"
></el-input>
</el-form-item>
</template>
<el-form-item prop="password" class="w-full">
<el-input
type="password"
v-model.trim="loginForm.password"
class="w-full"
size="large"
:placeholder="$t('commons.login.password')"
></el-input>
</el-form-item>
<el-row :gutter="10">
<el-col :span="12" v-if="!globalStore.ignoreCaptcha">
<el-form-item prop="captcha">
<el-input
v-model.trim="loginForm.captcha"
size="large"
:placeholder="$t('commons.login.captchaHelper')"
></el-input>
</el-form-item>
</el-col>
<el-col :span="12" v-if="!globalStore.ignoreCaptcha">
<img
class="w-full h-10"
v-if="captcha.imagePath"
:src="captcha.imagePath"
:alt="$t('commons.login.captchaHelper')"
@click="loginVerify()"
/>
</el-col>
<el-col :span="24" class="h-1">
<span v-show="errCaptcha" class="input-error">
{{ $t('commons.login.errorCaptcha') }}
</span>
<span v-show="errAuthInfo" class="input-error">
{{ $t('commons.login.errorAuthInfo') }}
</span>
</el-col>
</el-row>
<el-form-item>
<el-button
@click="login(loginFormRef)"
@focus="loginButtonFocused = true"
@blur="loginButtonFocused = false"
class="w-full"
type="primary"
size="default"
>
{{ $t('commons.button.login') }}
</el-button>
</el-form-item>
<template v-if="!isIntl">
<el-form-item prop="agreeLicense">
<el-checkbox v-model="loginForm.agreeLicense">
<template #default>
<span>
{{ $t('commons.button.agree') }}
<a
class="agree"
href="https://www.fit2cloud.com/legal/licenses.html"
target="_blank"
>
{{ $t('commons.login.licenseHelper') }}
</a>
</span>
</template>
</el-checkbox>
</el-form-item>
</template>
</div>
</el-form>
<div class="demo">
<span v-if="isDemo">
@ -146,28 +155,28 @@
</span>
</div>
</div>
</div>
<DialogPro v-model="open" center size="w-90">
<el-row type="flex" justify="center">
<span class="text-base mb-4">
{{ $t('commons.login.agreeTitle') }}
</span>
</el-row>
<div>
<span v-html="$t('commons.login.agreeContent')"></span>
</div>
<template #footer>
<span class="dialog-footer login-footer-btn">
<el-button @click="open = false">
{{ $t('commons.button.notAgree') }}
</el-button>
<el-button type="primary" @click="agreeWithLogin()">
{{ $t('commons.button.agree') }}
</el-button>
</span>
</template>
</DialogPro>
<DialogPro v-model="open" center size="w-90">
<el-row type="flex" justify="center">
<span class="text-base mb-4">
{{ $t('commons.login.agreeTitle') }}
</span>
</el-row>
<div>
<span v-html="$t('commons.login.agreeContent')"></span>
</div>
<template #footer>
<span class="dialog-footer login-footer-btn">
<el-button @click="open = false">
{{ $t('commons.button.notAgree') }}
</el-button>
<el-button type="primary" @click="agreeWithLogin()">
{{ $t('commons.button.agree') }}
</el-button>
</span>
</template>
</DialogPro>
</div>
</div>
</template>
@ -319,22 +328,8 @@ const login = (formEl: FormInstance | undefined) => {
isLoggingIn = true;
loading.value = true;
const res = await loginApi(requestLoginForm);
if (res.code === 406) {
if (res.message === 'ErrCaptchaCode') {
loginForm.captcha = '';
errCaptcha.value = true;
errAuthInfo.value = false;
}
if (res.message === 'ErrAuth') {
globalStore.ignoreCaptcha = false;
errCaptcha.value = false;
errAuthInfo.value = true;
}
loginVerify();
return;
}
globalStore.ignoreCaptcha = true;
if (res.data.mfaStatus === 'enable') {
if (res.data.mfaStatus === 'Enable') {
mfaShow.value = true;
errMfaInfo.value = false;
return;
@ -346,7 +341,20 @@ const login = (formEl: FormInstance | undefined) => {
MsgSuccess(i18n.t('commons.msg.loginSuccess'));
loadDataFromDB();
router.push({ name: 'home' });
} catch (error) {
} catch (res) {
if (res.code === 401) {
if (res.message === 'ErrCaptchaCode') {
loginForm.captcha = '';
errCaptcha.value = true;
errAuthInfo.value = false;
}
if (res.message === 'ErrAuth') {
globalStore.ignoreCaptcha = false;
errCaptcha.value = false;
errAuthInfo.value = true;
console.log('11111');
}
}
loginVerify();
} finally {
isLoggingIn = false;
@ -361,18 +369,23 @@ const mfaLogin = async (auto: boolean) => {
isLoggingIn = true;
mfaLoginForm.name = loginForm.name;
mfaLoginForm.password = loginForm.password;
const res = await mfaLoginApi(mfaLoginForm);
if (res.code === 406) {
errMfaInfo.value = true;
try {
await mfaLoginApi(mfaLoginForm);
globalStore.setLogStatus(true);
menuStore.setMenuList([]);
tabsStore.removeAllTabs();
MsgSuccess(i18n.t('commons.msg.loginSuccess'));
loadDataFromDB();
router.push({ name: 'home' });
} catch (res) {
if (res.code === 401) {
errMfaInfo.value = true;
isLoggingIn = false;
return;
}
} finally {
isLoggingIn = false;
return;
}
globalStore.setLogStatus(true);
menuStore.setMenuList([]);
tabsStore.removeAllTabs();
MsgSuccess(i18n.t('commons.msg.loginSuccess'));
loadDataFromDB();
router.push({ name: 'home' });
}
};
const loginVerify = async () => {
@ -430,9 +443,28 @@ onMounted(() => {
};
});
</script>
<style scoped lang="scss">
.login-form {
.agree {
text-decoration: none;
}
.agree:hover {
text-decoration: underline;
}
:deep(.el-button) {
height: 2.5rem;
}
:deep(.el-input__inner) {
-webkit-box-shadow: 0 0 0px 1000px transparent inset !important;
transition: background-color 50000s ease-in-out 0s;
}
:deep(.el-row) {
padding: 0 !important;
}
</style>
<!-- <style scoped lang='scss' > .login-form {
padding: 0 40px;
.hide {
width: 0;
@ -601,3 +633,4 @@ onMounted(() => {
}
}
</style>
-->

View File

@ -1,5 +1,25 @@
<template>
<div>
<div class="flex items-center justify-center min-h-screen relative bg-gray-100">
<div
class="absolute inset-0 bg-cover bg-center bg-no-repeat"
:style="{ backgroundImage: `url(${backgroundImage})` }"
></div>
<div
:style="{ opacity: backgroundOpacity }"
class="w-[45%] min-h-[480px] bg-white rounded-lg shadow-lg relative z-10 border border-gray-200 flex overflow-hidden"
>
<div class="grid md:grid-cols-2 gap-4 items-stretch w-full">
<div class="flex flex-col justify-center items-center w-full p-4">
<img :src="logoImage" class="max-w-full max-h-full object-contain" />
</div>
<div class="hidden md:block w-px bg-gray-200 absolute left-1/2 top-4 bottom-4"></div>
<div class="hidden md:flex items-center justify-center p-4">
<LoginForm ref="loginRef"></LoginForm>
</div>
</div>
</div>
</div>
<!-- <div>
<div class="login-background" v-loading="loading">
<div class="login-wrapper">
<div :class="screenWidth > 1110 ? 'left inline-block' : ''">
@ -15,7 +35,7 @@
</div>
</div>
</div>
</div>
</div> -->
</template>
<script setup lang="ts" name="login">
@ -28,6 +48,9 @@ import { getXpackSettingForTheme } from '@/utils/xpack';
const gStore = GlobalStore();
const loading = ref();
const backgroundOpacity = ref(1);
const backgroundImage = ref(new URL('', import.meta.url).href);
const logoImage = ref(new URL('@/assets/images/1panel-login.png', import.meta.url).href);
const mySafetyCode = defineProps({
code: {
@ -69,7 +92,7 @@ onMounted(() => {
});
</script>
<style scoped lang="scss">
<!-- <style scoped lang="scss">
@mixin login-center {
display: flex;
justify-content: center;
@ -153,4 +176,4 @@ onMounted(() => {
}
}
}
</style>
</style> -->

View File

@ -88,6 +88,9 @@ interface DialogProps {
const emit = defineEmits<{ (e: 'search'): void }>();
const acceptParams = (params: DialogProps): void => {
form.interval = params.interval;
if (params.interval == 0) {
form.interval = 30;
}
loadMfaCode();
drawerVisible.value = true;
};