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:
parent
b2fa0aa2d4
commit
88684c7cf0
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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})
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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>
|
||||
|
@ -177,8 +177,7 @@ const message = {
|
||||
mfaTitle: 'MFA Certification',
|
||||
mfaCode: 'MFA verification code',
|
||||
title: 'Linux Server Management Panel',
|
||||
licenseHelper:
|
||||
'Agree « <a href="https://www.fit2cloud.com/legal/licenses.html" target="_blank">Community License Agreement</a> »',
|
||||
licenseHelper: '<Community License Agreement>',
|
||||
errorAgree: 'Click to agree to the Community Software License',
|
||||
logout: 'Logout',
|
||||
agreeTitle: 'Agreement',
|
||||
|
@ -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: '合意',
|
||||
|
@ -166,8 +166,7 @@ const message = {
|
||||
mfaTitle: 'MFA 인증',
|
||||
mfaCode: 'MFA 인증 코드',
|
||||
title: 'Linux 서버 관리 패널',
|
||||
licenseHelper:
|
||||
'« <a href="https://www.fit2cloud.com/legal/licenses.html" target="_blank">커뮤니티 라이선스 계약</a> »에 동의합니다',
|
||||
licenseHelper: '<커뮤니티 라이선스 계약>',
|
||||
errorAgree: '커뮤니티 소프트웨어 라이선스에 동의하려면 클릭하세요',
|
||||
logout: '로그아웃',
|
||||
agreeTitle: '동의',
|
||||
|
@ -169,8 +169,7 @@ const message = {
|
||||
mfaTitle: 'Pengesahan MFA',
|
||||
mfaCode: 'Kod pengesahan MFA',
|
||||
title: 'Panel Pengurusan Pelayan Linux',
|
||||
licenseHelper:
|
||||
'Setuju « <a href="https://www.fit2cloud.com/legal/licenses.html" target="_blank">Perjanjian Lesen Komuniti</a> »',
|
||||
licenseHelper: '<Perjanjian Lesen Komuniti>',
|
||||
errorAgree: 'Klik untuk bersetuju dengan Lesen Perisian Komuniti',
|
||||
logout: 'Log keluar',
|
||||
agreeTitle: 'Agreement',
|
||||
|
@ -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 « <a href="https://www.fit2cloud.com/legal/licenses.html" target="_blank">Acordo de Licença Comunitária</a> »',
|
||||
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',
|
||||
|
@ -167,8 +167,7 @@ const message = {
|
||||
mfaTitle: 'MFA Сертификация',
|
||||
mfaCode: 'MFA код подтверждения',
|
||||
title: 'Панель управления Linux сервером',
|
||||
licenseHelper:
|
||||
'Согласен « <a href="https://www.fit2cloud.com/legal/licenses.html" target="_blank">Лицензионное соглашение сообщества</a> »',
|
||||
licenseHelper: '<Лицензионное соглашение сообщества>',
|
||||
errorAgree: 'Нажмите, чтобы согласиться с Лицензией программного обеспечения сообщества',
|
||||
logout: 'Выход',
|
||||
agreeTitle: 'Соглашение',
|
||||
|
@ -175,8 +175,7 @@ const message = {
|
||||
mfaTitle: 'MFA 認證',
|
||||
mfaCode: 'MFA 驗證碼',
|
||||
title: 'Linux 服務器運維管理面板',
|
||||
licenseHelper:
|
||||
'同意 « <a href="https://www.fit2cloud.com/legal/licenses.html" target="_blank"> 飛致雲社區軟件許可協議</a> »',
|
||||
licenseHelper: '《飛致雲社區軟件許可協議》',
|
||||
errorAgree: '請點擊同意社區軟件許可協議',
|
||||
agreeTitle: '服務協議及隱私保護',
|
||||
agreeContent:
|
||||
|
@ -175,8 +175,7 @@ const message = {
|
||||
mfaTitle: 'MFA 认证',
|
||||
mfaCode: 'MFA 验证码',
|
||||
title: 'Linux 服务器运维管理面板',
|
||||
licenseHelper:
|
||||
'同意 « <a href="https://www.fit2cloud.com/legal/licenses.html" target="_blank"> 飞致云社区软件许可协议</a> »',
|
||||
licenseHelper: '《飞致云社区软件许可协议》',
|
||||
errorAgree: '请点击同意社区软件许可协议',
|
||||
agreeTitle: '服务协议及隐私保护',
|
||||
agreeContent:
|
||||
|
@ -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 = {
|
||||
|
@ -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);
|
||||
|
@ -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>
|
||||
-->
|
||||
|
@ -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> -->
|
||||
|
@ -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;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user