mirror of
https://github.com/1Panel-dev/MaxKB.git
synced 2025-12-25 17:22:55 +00:00
feat: Login and add graphic captcha (#3117)
This commit is contained in:
parent
1ba8077e95
commit
c1ddec1a61
|
|
@ -11,6 +11,7 @@ import importlib
|
||||||
import io
|
import io
|
||||||
import mimetypes
|
import mimetypes
|
||||||
import pickle
|
import pickle
|
||||||
|
import random
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
|
|
@ -297,3 +298,10 @@ def markdown_to_plain_text(md: str) -> str:
|
||||||
# 去除首尾空格
|
# 去除首尾空格
|
||||||
text = text.strip()
|
text = text.strip()
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
CHAR_SET = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
|
||||||
|
|
||||||
|
|
||||||
|
def get_random_chars(number=6):
|
||||||
|
return "".join([CHAR_SET[random.randint(0, len(CHAR_SET) - 1)] for index in range(number)])
|
||||||
|
|
|
||||||
|
|
@ -7490,4 +7490,13 @@ msgid "Field: {name} No value set"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Generate related"
|
msgid "Generate related"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Obtain graphical captcha"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Captcha code error or expiration"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "captcha"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
@ -7653,4 +7653,13 @@ msgid "Field: {name} No value set"
|
||||||
msgstr "字段: {name} 未设置值"
|
msgstr "字段: {name} 未设置值"
|
||||||
|
|
||||||
msgid "Generate related"
|
msgid "Generate related"
|
||||||
msgstr "生成问题"
|
msgstr "生成问题"
|
||||||
|
|
||||||
|
msgid "Obtain graphical captcha"
|
||||||
|
msgstr "获取图形验证码"
|
||||||
|
|
||||||
|
msgid "Captcha code error or expiration"
|
||||||
|
msgstr "验证码错误或过期"
|
||||||
|
|
||||||
|
msgid "captcha"
|
||||||
|
msgstr "验证码"
|
||||||
|
|
@ -7663,4 +7663,13 @@ msgid "Field: {name} No value set"
|
||||||
msgstr "欄位: {name} 未設定值"
|
msgstr "欄位: {name} 未設定值"
|
||||||
|
|
||||||
msgid "Generate related"
|
msgid "Generate related"
|
||||||
msgstr "生成問題"
|
msgstr "生成問題"
|
||||||
|
|
||||||
|
msgid "Obtain graphical captcha"
|
||||||
|
msgstr "獲取圖形驗證碼"
|
||||||
|
|
||||||
|
msgid "Captcha code error or expiration"
|
||||||
|
msgstr "驗證碼錯誤或過期"
|
||||||
|
|
||||||
|
msgid "captcha"
|
||||||
|
msgstr "驗證碼"
|
||||||
|
|
@ -126,6 +126,10 @@ CACHES = {
|
||||||
"token_cache": {
|
"token_cache": {
|
||||||
'BACKEND': 'common.cache.file_cache.FileCache',
|
'BACKEND': 'common.cache.file_cache.FileCache',
|
||||||
'LOCATION': os.path.join(PROJECT_DIR, 'data', 'cache', "token_cache") # 文件夹路径
|
'LOCATION': os.path.join(PROJECT_DIR, 'data', 'cache', "token_cache") # 文件夹路径
|
||||||
|
},
|
||||||
|
'captcha_cache': {
|
||||||
|
'BACKEND': 'common.cache.file_cache.FileCache',
|
||||||
|
'LOCATION': os.path.join(PROJECT_DIR, 'data', 'cache', "captcha_cache") # 文件夹路径
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,18 +6,22 @@
|
||||||
@date:2023/9/5 16:32
|
@date:2023/9/5 16:32
|
||||||
@desc:
|
@desc:
|
||||||
"""
|
"""
|
||||||
|
import base64
|
||||||
import datetime
|
import datetime
|
||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
import re
|
import re
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
from captcha.image import ImageCaptcha
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core import validators, signing, cache
|
from django.core import validators, signing, cache
|
||||||
from django.core.mail import send_mail
|
from django.core.mail import send_mail
|
||||||
from django.core.mail.backends.smtp import EmailBackend
|
from django.core.mail.backends.smtp import EmailBackend
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.db.models import Q, QuerySet, Prefetch
|
from django.db.models import Q, QuerySet, Prefetch
|
||||||
|
from django.utils.translation import get_language
|
||||||
|
from django.utils.translation import gettext_lazy as _, to_locale
|
||||||
from drf_yasg import openapi
|
from drf_yasg import openapi
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
|
@ -30,7 +34,7 @@ from common.exception.app_exception import AppApiException
|
||||||
from common.mixins.api_mixin import ApiMixin
|
from common.mixins.api_mixin import ApiMixin
|
||||||
from common.models.db_model_manage import DBModelManage
|
from common.models.db_model_manage import DBModelManage
|
||||||
from common.response.result import get_api_response
|
from common.response.result import get_api_response
|
||||||
from common.util.common import valid_license
|
from common.util.common import valid_license, get_random_chars
|
||||||
from common.util.field_message import ErrMessage
|
from common.util.field_message import ErrMessage
|
||||||
from common.util.lock import lock
|
from common.util.lock import lock
|
||||||
from dataset.models import DataSet, Document, Paragraph, Problem, ProblemParagraphMapping
|
from dataset.models import DataSet, Document, Paragraph, Problem, ProblemParagraphMapping
|
||||||
|
|
@ -39,9 +43,29 @@ from function_lib.models.function import FunctionLib
|
||||||
from setting.models import Team, SystemSetting, SettingType, Model, TeamMember, TeamMemberPermission
|
from setting.models import Team, SystemSetting, SettingType, Model, TeamMember, TeamMemberPermission
|
||||||
from smartdoc.conf import PROJECT_DIR
|
from smartdoc.conf import PROJECT_DIR
|
||||||
from users.models.user import User, password_encrypt, get_user_dynamics_permission
|
from users.models.user import User, password_encrypt, get_user_dynamics_permission
|
||||||
from django.utils.translation import gettext_lazy as _, gettext, to_locale
|
|
||||||
from django.utils.translation import get_language
|
|
||||||
user_cache = cache.caches['user_cache']
|
user_cache = cache.caches['user_cache']
|
||||||
|
captcha_cache = cache.caches['captcha_cache']
|
||||||
|
|
||||||
|
|
||||||
|
class CaptchaSerializer(ApiMixin, serializers.Serializer):
|
||||||
|
@staticmethod
|
||||||
|
def get_response_body_api():
|
||||||
|
return get_api_response(openapi.Schema(
|
||||||
|
type=openapi.TYPE_STRING,
|
||||||
|
title="captcha",
|
||||||
|
default="xxxx",
|
||||||
|
description="captcha"
|
||||||
|
))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def generate():
|
||||||
|
chars = get_random_chars()
|
||||||
|
image = ImageCaptcha()
|
||||||
|
data = image.generate(chars)
|
||||||
|
captcha = base64.b64encode(data.getbuffer())
|
||||||
|
captcha_cache.set(f"LOGIN:{chars}", chars, timeout=5 * 60)
|
||||||
|
return 'data:image/png;base64,' + captcha.decode()
|
||||||
|
|
||||||
|
|
||||||
class SystemSerializer(ApiMixin, serializers.Serializer):
|
class SystemSerializer(ApiMixin, serializers.Serializer):
|
||||||
|
|
@ -71,6 +95,8 @@ class LoginSerializer(ApiMixin, serializers.Serializer):
|
||||||
|
|
||||||
password = serializers.CharField(required=True, error_messages=ErrMessage.char(_("Password")))
|
password = serializers.CharField(required=True, error_messages=ErrMessage.char(_("Password")))
|
||||||
|
|
||||||
|
captcha = serializers.CharField(required=True, error_messages=ErrMessage.char(_("captcha")))
|
||||||
|
|
||||||
def is_valid(self, *, raise_exception=False):
|
def is_valid(self, *, raise_exception=False):
|
||||||
"""
|
"""
|
||||||
校验参数
|
校验参数
|
||||||
|
|
@ -78,6 +104,10 @@ class LoginSerializer(ApiMixin, serializers.Serializer):
|
||||||
:return: User information
|
:return: User information
|
||||||
"""
|
"""
|
||||||
super().is_valid(raise_exception=True)
|
super().is_valid(raise_exception=True)
|
||||||
|
captcha = self.data.get('captcha')
|
||||||
|
captcha_value = captcha_cache.get(f"LOGIN:{captcha}")
|
||||||
|
if captcha_value is None:
|
||||||
|
raise AppApiException(1005, _("Captcha code error or expiration"))
|
||||||
username = self.data.get("username")
|
username = self.data.get("username")
|
||||||
password = password_encrypt(self.data.get("password"))
|
password = password_encrypt(self.data.get("password"))
|
||||||
user = QuerySet(User).filter(Q(username=username,
|
user = QuerySet(User).filter(Q(username=username,
|
||||||
|
|
@ -109,7 +139,8 @@ class LoginSerializer(ApiMixin, serializers.Serializer):
|
||||||
required=['username', 'password'],
|
required=['username', 'password'],
|
||||||
properties={
|
properties={
|
||||||
'username': openapi.Schema(type=openapi.TYPE_STRING, title=_("Username"), description=_("Username")),
|
'username': openapi.Schema(type=openapi.TYPE_STRING, title=_("Username"), description=_("Username")),
|
||||||
'password': openapi.Schema(type=openapi.TYPE_STRING, title=_("Password"), description=_("Password"))
|
'password': openapi.Schema(type=openapi.TYPE_STRING, title=_("Password"), description=_("Password")),
|
||||||
|
'captcha': openapi.Schema(type=openapi.TYPE_STRING, title=_("captcha"), description=_("captcha"))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ app_name = "user"
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('profile', views.Profile.as_view()),
|
path('profile', views.Profile.as_view()),
|
||||||
path('user', views.User.as_view(), name="profile"),
|
path('user', views.User.as_view(), name="profile"),
|
||||||
|
path('user/captcha', views.CaptchaView.as_view(), name='captcha'),
|
||||||
path('user/language', views.SwitchUserLanguageView.as_view(), name='language'),
|
path('user/language', views.SwitchUserLanguageView.as_view(), name='language'),
|
||||||
path('user/list', views.User.Query.as_view()),
|
path('user/list', views.User.Query.as_view()),
|
||||||
path('user/login', views.Login.as_view(), name='login'),
|
path('user/login', views.Login.as_view(), name='login'),
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ from smartdoc.settings import JWT_AUTH
|
||||||
from users.serializers.user_serializers import RegisterSerializer, LoginSerializer, CheckCodeSerializer, \
|
from users.serializers.user_serializers import RegisterSerializer, LoginSerializer, CheckCodeSerializer, \
|
||||||
RePasswordSerializer, \
|
RePasswordSerializer, \
|
||||||
SendEmailSerializer, UserProfile, UserSerializer, UserManageSerializer, UserInstanceSerializer, SystemSerializer, \
|
SendEmailSerializer, UserProfile, UserSerializer, UserManageSerializer, UserInstanceSerializer, SystemSerializer, \
|
||||||
SwitchLanguageSerializer
|
SwitchLanguageSerializer, CaptchaSerializer
|
||||||
from users.views.common import get_user_operation_object, get_re_password_details
|
from users.views.common import get_user_operation_object, get_re_password_details
|
||||||
|
|
||||||
user_cache = cache.caches['user_cache']
|
user_cache = cache.caches['user_cache']
|
||||||
|
|
@ -170,6 +170,18 @@ def _get_details(request):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class CaptchaView(APIView):
|
||||||
|
|
||||||
|
@action(methods=['GET'], detail=False)
|
||||||
|
@swagger_auto_schema(operation_summary=_("Obtain graphical captcha"),
|
||||||
|
operation_id=_("Obtain graphical captcha"),
|
||||||
|
responses=CaptchaSerializer().get_response_body_api(),
|
||||||
|
security=[],
|
||||||
|
tags=[_("User management")])
|
||||||
|
def get(self, request: Request):
|
||||||
|
return result.success(CaptchaSerializer().generate())
|
||||||
|
|
||||||
|
|
||||||
class Login(APIView):
|
class Login(APIView):
|
||||||
|
|
||||||
@action(methods=['POST'], detail=False)
|
@action(methods=['POST'], detail=False)
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,7 @@ django-db-connection-pool = "1.2.5"
|
||||||
opencv-python-headless = "4.11.0.86"
|
opencv-python-headless = "4.11.0.86"
|
||||||
pymysql = "1.1.1"
|
pymysql = "1.1.1"
|
||||||
accelerate = "1.6.0"
|
accelerate = "1.6.0"
|
||||||
|
captcha = "0.7.1"
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["poetry-core"]
|
requires = ["poetry-core"]
|
||||||
build-backend = "poetry.core.masonry.api"
|
build-backend = "poetry.core.masonry.api"
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,10 @@ interface LoginRequest {
|
||||||
* 密码
|
* 密码
|
||||||
*/
|
*/
|
||||||
password: string
|
password: string
|
||||||
|
/**
|
||||||
|
* 验证码
|
||||||
|
*/
|
||||||
|
captcha: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface RegisterRequest {
|
interface RegisterRequest {
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,13 @@ const login: (
|
||||||
}
|
}
|
||||||
return post('/user/login', request, undefined, loading)
|
return post('/user/login', request, undefined, loading)
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 获取图形验证码
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const getCaptcha: () => Promise<Result<string>> = () => {
|
||||||
|
return get('user/captcha')
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* 登出
|
* 登出
|
||||||
* @param loading 接口加载器
|
* @param loading 接口加载器
|
||||||
|
|
@ -226,5 +233,6 @@ export default {
|
||||||
postLanguage,
|
postLanguage,
|
||||||
getDingOauth2Callback,
|
getDingOauth2Callback,
|
||||||
getlarkCallback,
|
getlarkCallback,
|
||||||
getQrSource
|
getQrSource,
|
||||||
|
getCaptcha
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,10 @@ export default {
|
||||||
requiredMessage: 'Please enter username',
|
requiredMessage: 'Please enter username',
|
||||||
lengthMessage: 'Length must be between 6 and 20 words'
|
lengthMessage: 'Length must be between 6 and 20 words'
|
||||||
},
|
},
|
||||||
|
captcha: {
|
||||||
|
label: 'captcha',
|
||||||
|
placeholder: 'Please enter the captcha'
|
||||||
|
},
|
||||||
nick_name: {
|
nick_name: {
|
||||||
label: 'Name',
|
label: 'Name',
|
||||||
placeholder: 'Please enter name'
|
placeholder: 'Please enter name'
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,10 @@ export default {
|
||||||
requiredMessage: '请输入用户名',
|
requiredMessage: '请输入用户名',
|
||||||
lengthMessage: '长度在 6 到 20 个字符'
|
lengthMessage: '长度在 6 到 20 个字符'
|
||||||
},
|
},
|
||||||
|
captcha: {
|
||||||
|
label: '验证码',
|
||||||
|
placeholder: '请输入验证码'
|
||||||
|
},
|
||||||
nick_name: {
|
nick_name: {
|
||||||
label: '姓名',
|
label: '姓名',
|
||||||
placeholder: '请输入姓名'
|
placeholder: '请输入姓名'
|
||||||
|
|
@ -33,7 +37,7 @@ export default {
|
||||||
label: '邮箱',
|
label: '邮箱',
|
||||||
placeholder: '请输入邮箱',
|
placeholder: '请输入邮箱',
|
||||||
requiredMessage: '请输入邮箱',
|
requiredMessage: '请输入邮箱',
|
||||||
validatorEmail: '请输入有效邮箱格式!',
|
validatorEmail: '请输入有效邮箱格式!'
|
||||||
},
|
},
|
||||||
phone: {
|
phone: {
|
||||||
label: '手机号',
|
label: '手机号',
|
||||||
|
|
@ -48,13 +52,13 @@ export default {
|
||||||
new_password: {
|
new_password: {
|
||||||
label: '新密码',
|
label: '新密码',
|
||||||
placeholder: '请输入新密码',
|
placeholder: '请输入新密码',
|
||||||
requiredMessage: '请输入新密码',
|
requiredMessage: '请输入新密码'
|
||||||
},
|
},
|
||||||
re_password: {
|
re_password: {
|
||||||
label: '确认密码',
|
label: '确认密码',
|
||||||
placeholder: '请输入确认密码',
|
placeholder: '请输入确认密码',
|
||||||
requiredMessage: '请输入确认密码',
|
requiredMessage: '请输入确认密码',
|
||||||
validatorMessage: '密码不一致',
|
validatorMessage: '密码不一致'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,10 @@ export default {
|
||||||
requiredMessage: '請輸入使用者名稱',
|
requiredMessage: '請輸入使用者名稱',
|
||||||
lengthMessage: '長度須介於 6 到 20 個字元之間'
|
lengthMessage: '長度須介於 6 到 20 個字元之間'
|
||||||
},
|
},
|
||||||
|
captcha: {
|
||||||
|
label: '驗證碼',
|
||||||
|
placeholder: '請輸入驗證碼'
|
||||||
|
},
|
||||||
nick_name: {
|
nick_name: {
|
||||||
label: '姓名',
|
label: '姓名',
|
||||||
placeholder: '請輸入姓名'
|
placeholder: '請輸入姓名'
|
||||||
|
|
|
||||||
|
|
@ -135,8 +135,8 @@ const useUserStore = defineStore({
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
async login(auth_type: string, username: string, password: string) {
|
async login(auth_type: string, username: string, password: string, captcha: string) {
|
||||||
return UserApi.login(auth_type, { username, password }).then((ok) => {
|
return UserApi.login(auth_type, { username, password, captcha }).then((ok) => {
|
||||||
this.token = ok.data
|
this.token = ok.data
|
||||||
localStorage.setItem('token', ok.data)
|
localStorage.setItem('token', ok.data)
|
||||||
return this.profile()
|
return this.profile()
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,21 @@
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mb-24">
|
||||||
|
<el-form-item prop="captcha">
|
||||||
|
<div class="flex-between w-full">
|
||||||
|
<el-input
|
||||||
|
size="large"
|
||||||
|
class="input-item"
|
||||||
|
v-model="loginForm.captcha"
|
||||||
|
:placeholder="$t('views.user.userForm.form.captcha.placeholder')"
|
||||||
|
>
|
||||||
|
</el-input>
|
||||||
|
|
||||||
|
<img :src="identifyCode" alt="" height="40" class="ml-8 cursor" @click="makeCode" />
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
<el-button size="large" type="primary" class="w-full" @click="login"
|
<el-button size="large" type="primary" class="w-full" @click="login"
|
||||||
|
|
@ -107,6 +122,7 @@ import { useRoute, useRouter } from 'vue-router'
|
||||||
import type { FormInstance, FormRules } from 'element-plus'
|
import type { FormInstance, FormRules } from 'element-plus'
|
||||||
import useStore from '@/stores'
|
import useStore from '@/stores'
|
||||||
import authApi from '@/api/auth-setting'
|
import authApi from '@/api/auth-setting'
|
||||||
|
import useApi from '@/api/user'
|
||||||
import { MsgConfirm, MsgError, MsgSuccess } from '@/utils/message'
|
import { MsgConfirm, MsgError, MsgSuccess } from '@/utils/message'
|
||||||
|
|
||||||
import { t, getBrowserLang } from '@/locales'
|
import { t, getBrowserLang } from '@/locales'
|
||||||
|
|
@ -120,9 +136,15 @@ const { user } = useStore()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const loginForm = ref<LoginRequest>({
|
const loginForm = ref<LoginRequest>({
|
||||||
username: '',
|
username: '',
|
||||||
password: ''
|
password: '',
|
||||||
|
captcha: ''
|
||||||
})
|
})
|
||||||
|
const identifyCode = ref<string>('')
|
||||||
|
function makeCode() {
|
||||||
|
useApi.getCaptcha().then((res: any) => {
|
||||||
|
identifyCode.value = res.data
|
||||||
|
})
|
||||||
|
}
|
||||||
const rules = ref<FormRules<LoginRequest>>({
|
const rules = ref<FormRules<LoginRequest>>({
|
||||||
username: [
|
username: [
|
||||||
{
|
{
|
||||||
|
|
@ -137,6 +159,13 @@ const rules = ref<FormRules<LoginRequest>>({
|
||||||
message: t('views.user.userForm.form.password.requiredMessage'),
|
message: t('views.user.userForm.form.password.requiredMessage'),
|
||||||
trigger: 'blur'
|
trigger: 'blur'
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
captcha: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: t('views.user.userForm.form.captcha.placeholder'),
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
const loginFormRef = ref<FormInstance>()
|
const loginFormRef = ref<FormInstance>()
|
||||||
|
|
@ -222,7 +251,8 @@ function changeMode(val: string) {
|
||||||
showQrCodeTab.value = false
|
showQrCodeTab.value = false
|
||||||
loginForm.value = {
|
loginForm.value = {
|
||||||
username: '',
|
username: '',
|
||||||
password: ''
|
password: '',
|
||||||
|
captcha: ''
|
||||||
}
|
}
|
||||||
redirectAuth(val)
|
redirectAuth(val)
|
||||||
loginFormRef.value?.clearValidate()
|
loginFormRef.value?.clearValidate()
|
||||||
|
|
@ -232,7 +262,12 @@ const login = () => {
|
||||||
loginFormRef.value?.validate().then(() => {
|
loginFormRef.value?.validate().then(() => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
user
|
user
|
||||||
.login(loginMode.value, loginForm.value.username, loginForm.value.password)
|
.login(
|
||||||
|
loginMode.value,
|
||||||
|
loginForm.value.username,
|
||||||
|
loginForm.value.password,
|
||||||
|
loginForm.value.captcha
|
||||||
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
locale.value = localStorage.getItem('MaxKB-locale') || getBrowserLang() || 'en-US'
|
locale.value = localStorage.getItem('MaxKB-locale') || getBrowserLang() || 'en-US'
|
||||||
router.push({ name: 'home' })
|
router.push({ name: 'home' })
|
||||||
|
|
@ -285,6 +320,7 @@ onBeforeMount(() => {
|
||||||
declare const window: any
|
declare const window: any
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
makeCode()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const currentUrl = ref(route.fullPath)
|
const currentUrl = ref(route.fullPath)
|
||||||
const params = new URLSearchParams(currentUrl.value.split('?')[1])
|
const params = new URLSearchParams(currentUrl.value.split('?')[1])
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue