You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
415 lines
11 KiB
415 lines
11 KiB
<template>
|
|
<div class="login-container">
|
|
<el-form
|
|
ref="formLogin"
|
|
:model="loginForm"
|
|
:rules="loginFormRules"
|
|
label-position="left"
|
|
label-width="0px"
|
|
class="demo-ruleForm login-page"
|
|
>
|
|
<div class="title-container">
|
|
<h3 class="title">
|
|
{{ $t('login.title') }}
|
|
</h3>
|
|
<lang-select class="set-language" />
|
|
</div>
|
|
<el-form-item label-width="0px">
|
|
<tenant-box
|
|
v-if="isMultiEnabled"
|
|
v-model="loginForm.tenantName"
|
|
/>
|
|
</el-form-item>
|
|
<el-tabs
|
|
stretch
|
|
@tab-click="handleLoginTabChanged"
|
|
>
|
|
<el-tab-pane :label="$t('login.userLogin')">
|
|
<div v-if="loginType === 'password'">
|
|
<el-form-item
|
|
prop="username"
|
|
>
|
|
<el-input
|
|
v-model="loginForm.username"
|
|
prefix-icon="el-icon-user"
|
|
type="text"
|
|
auto-complete="off"
|
|
tabindex="1"
|
|
:placeholder="$t('global.pleaseInputBy', {key: $t('AbpAccount.UserNameOrEmailAddress')})"
|
|
/>
|
|
</el-form-item>
|
|
<el-form-item
|
|
prop="password"
|
|
>
|
|
<el-input
|
|
:key="passwordType"
|
|
ref="password"
|
|
v-model="loginForm.password"
|
|
prefix-icon="el-icon-lock"
|
|
:type="passwordType"
|
|
:placeholder="$t('global.pleaseInputBy', {key: $t('AbpAccount.Password')})"
|
|
name="password"
|
|
tabindex="2"
|
|
@keyup.enter.native="handleUserLogin"
|
|
/>
|
|
<span
|
|
class="show-pwd"
|
|
@click="showPwd"
|
|
>
|
|
<svg-icon :name="passwordType === 'password' ? 'eye-off' : 'eye-on'" />
|
|
</span>
|
|
</el-form-item>
|
|
</div>
|
|
</el-tab-pane>
|
|
<el-tab-pane :label="$t('login.phoneLogin')">
|
|
<div v-if="loginType === 'phone'">
|
|
<el-form-item
|
|
prop="phoneNumber"
|
|
>
|
|
<el-input
|
|
ref="loginItemPhone"
|
|
v-model="loginForm.phoneNumber"
|
|
prefix-icon="el-icon-mobile-phone"
|
|
type="text"
|
|
maxlength="11"
|
|
auto-complete="off"
|
|
:placeholder="$t('global.pleaseInputBy', {key: $t('login.phoneNumber')})"
|
|
/>
|
|
</el-form-item>
|
|
<el-form-item
|
|
prop="verifyCode"
|
|
>
|
|
<el-row>
|
|
<el-col :span="16">
|
|
<el-input
|
|
v-model="loginForm.verifyCode"
|
|
auto-complete="off"
|
|
:placeholder="$t('global.pleaseInputBy', {key: $t('login.phoneVerifyCode')})"
|
|
prefix-icon="el-icon-key"
|
|
style="margin:-right: 10px;"
|
|
/>
|
|
</el-col>
|
|
<el-col :span="8">
|
|
<el-button
|
|
ref="sendButton"
|
|
style="margin-left: 10px;width: 132px;"
|
|
:disabled="sending"
|
|
@click="handleSendPhoneVerifyCode"
|
|
>
|
|
{{ sendButtonName }}
|
|
</el-button>
|
|
</el-col>
|
|
</el-row>
|
|
</el-form-item>
|
|
</div>
|
|
</el-tab-pane>
|
|
</el-tabs>
|
|
|
|
<el-row>
|
|
<el-col :span="12">
|
|
<el-form-item
|
|
v-show="selfRegistration"
|
|
label-width="100px"
|
|
:label="$t('AbpAccount.AreYouANewUser')"
|
|
>
|
|
<el-link
|
|
type="success"
|
|
@click="handleRedirectRegister"
|
|
>
|
|
{{ $t('AbpAccount.Register') }}
|
|
</el-link>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="12">
|
|
<el-form-item
|
|
label-width="100px"
|
|
:label="$t('AbpAccount.ForgotPassword')"
|
|
>
|
|
<el-link
|
|
type="info"
|
|
@click="handleRedirectResetPassword"
|
|
>
|
|
{{ $t('AbpAccount.ResetPassword') }}
|
|
</el-link>
|
|
</el-form-item>
|
|
</el-col>
|
|
</el-row>
|
|
|
|
<el-form-item style="width:100%;">
|
|
<el-button
|
|
type="primary"
|
|
style="width:100%;"
|
|
:loading="logining"
|
|
@click="handleUserLogin"
|
|
>
|
|
{{ $t('AbpAccount.Login') }}
|
|
</el-button>
|
|
</el-form-item>
|
|
</el-form>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import { Input } from 'element-ui'
|
|
import { Route } from 'vue-router'
|
|
import { UserModule } from '@/store/modules/user'
|
|
import { Dictionary } from 'vue-router/types/router'
|
|
import TenantBox from '@/components/TenantBox/index.vue'
|
|
import LangSelect from '@/components/LangSelect/index.vue'
|
|
import { Component, Vue, Watch } from 'vue-property-decorator'
|
|
import UserService from '@/api/users'
|
|
import { AbpModule } from '@/store/modules/abp'
|
|
|
|
@Component({
|
|
name: 'Login',
|
|
components: {
|
|
LangSelect,
|
|
TenantBox
|
|
}
|
|
})
|
|
export default class extends Vue {
|
|
private loginType = 'password'
|
|
private passwordType = 'password'
|
|
private redirect?: string
|
|
|
|
private sendTimer: any
|
|
private sending = false
|
|
private sendButtonName = this.l('login.sendVerifyCode')
|
|
private logining = false
|
|
private loginForm = {
|
|
tenantName: AbpModule.configuration?.currentTenant?.name,
|
|
username: '',
|
|
password: '',
|
|
phoneNumber: '',
|
|
verifyCode: ''
|
|
}
|
|
|
|
get isMultiEnabled() {
|
|
return AbpModule.configuration?.multiTenancy?.isEnabled
|
|
}
|
|
|
|
get selfRegistration() {
|
|
const settingValues = AbpModule.configuration?.setting?.values
|
|
if (settingValues && settingValues['Abp.Account.IsSelfRegistrationEnabled'] === true) {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
private validatePhoneNumberValue = (rule: any, value: string, callback: any) => {
|
|
const phoneReg = /^1[34578]\d{9}$/
|
|
if (!value || !phoneReg.test(value)) {
|
|
callback(new Error(this.l('global.pleaseInputBy', { key: this.l('global.correctPhoneNumber') })))
|
|
} else {
|
|
callback()
|
|
}
|
|
}
|
|
|
|
private loginFormRules = {
|
|
username: [
|
|
{
|
|
required: true, message: this.l('global.pleaseInputBy', { key: this.l('AbpAccount.UserNameOrEmailAddress') }), trigger: 'blur'
|
|
}
|
|
],
|
|
password: [
|
|
{
|
|
required: true, message: this.l('global.pleaseInputBy', { key: this.l('AbpAccount.Password') }), trigger: 'blur'
|
|
}
|
|
],
|
|
phoneNumber: [
|
|
{
|
|
required: true, validator: this.validatePhoneNumberValue, trigger: 'blur'
|
|
}
|
|
],
|
|
verifyCode: [
|
|
{
|
|
required: true, message: this.l('global.pleaseInputBy', { key: this.l('login.phoneVerifyCode') }), trigger: 'blur'
|
|
}
|
|
]
|
|
}
|
|
|
|
destroyed() {
|
|
if (this.sendTimer) {
|
|
clearInterval(this.sendTimer)
|
|
}
|
|
}
|
|
|
|
@Watch('$route', { immediate: true })
|
|
private onRouteChange(route: Route) {
|
|
// TODO: remove the "as Dictionary<string>" hack after v4 release for vue-router
|
|
// See https://github.com/vuejs/vue-router/pull/2050 for details
|
|
const query = route.query as Dictionary<string>
|
|
if (query) {
|
|
this.redirect = query.redirect
|
|
}
|
|
}
|
|
|
|
private showPwd() {
|
|
if (this.passwordType === 'password') {
|
|
this.passwordType = ''
|
|
} else {
|
|
this.passwordType = 'password'
|
|
}
|
|
this.$nextTick(() => {
|
|
(this.$refs.password as Input).focus()
|
|
})
|
|
}
|
|
|
|
private handleRedirectRegister() {
|
|
this.$router.replace('register')
|
|
}
|
|
|
|
private handleRedirectResetPassword() {
|
|
this.$router.replace('reset-password')
|
|
}
|
|
|
|
private handleUserLogin() {
|
|
const frmLogin = this.$refs.formLogin as any
|
|
frmLogin.validate(async(valid: boolean) => {
|
|
if (valid) {
|
|
this.logining = true
|
|
try {
|
|
if (this.loginType === 'password') {
|
|
const userLogin = {
|
|
username: this.loginForm.username,
|
|
password: this.loginForm.password
|
|
}
|
|
await UserModule.Login(userLogin)
|
|
this.$router.push({
|
|
path: this.redirect || '/'
|
|
})
|
|
} else {
|
|
const phoneLogin = {
|
|
phoneNumber: this.loginForm.phoneNumber,
|
|
verifyCode: this.loginForm.verifyCode
|
|
}
|
|
await UserModule.PhoneLogin(phoneLogin)
|
|
this.$router.push({
|
|
path: this.redirect || '/'
|
|
})
|
|
}
|
|
} catch {
|
|
this.resetLoginButton()
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
private handleSendPhoneVerifyCode() {
|
|
const frmLogin = this.$refs.formLogin as any
|
|
frmLogin.validateField('phoneNumber', (errorMsg: string) => {
|
|
if (!errorMsg) {
|
|
this.sending = true
|
|
UserService.sendSmsSigninCode(this.loginForm.phoneNumber).then(() => {
|
|
let interValTime = 60
|
|
const sendingName = this.l('login.afterSendVerifyCode')
|
|
const sendedName = this.l('login.sendVerifyCode')
|
|
this.sendTimer = setInterval(() => {
|
|
this.sendButtonName = interValTime + sendingName
|
|
--interValTime
|
|
if (interValTime < 0) {
|
|
this.sendButtonName = sendedName
|
|
this.sending = false
|
|
clearInterval(this.sendTimer)
|
|
}
|
|
}, 1000)
|
|
}).catch(() => {
|
|
this.sending = false
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
private handleLoginTabChanged(tab: any) {
|
|
this.loginType = tab.paneName === '1' ? 'phone' : 'password'
|
|
}
|
|
|
|
private l(name: string, values?: any[] | { [key: string]: any }) {
|
|
return this.$t(name, values).toString()
|
|
}
|
|
|
|
private resetLoginButton() {
|
|
setTimeout(() => {
|
|
this.logining = false
|
|
}, 0.5 * 1000)
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.login-container {
|
|
width: 100%;
|
|
height: 100%;
|
|
overflow-x: hidden;
|
|
overflow-y: auto;
|
|
background-color: $loginBg;
|
|
|
|
.svg-container {
|
|
padding: 6px 5px 6px 15px;
|
|
color: $darkGray;
|
|
vertical-align: middle;
|
|
width: 30px;
|
|
display: inline-block;
|
|
}
|
|
|
|
.title-container {
|
|
position: relative;
|
|
|
|
.title {
|
|
font-size: 26px;
|
|
margin: 0px auto 20px auto;
|
|
text-align: center;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.tips {
|
|
font-size: 14px;
|
|
color: #fff;
|
|
margin-bottom: 10px;
|
|
|
|
span {
|
|
&:first-of-type {
|
|
margin-right: 16px;
|
|
}
|
|
}
|
|
}
|
|
|
|
.set-language {
|
|
position: absolute;
|
|
top: 3px;
|
|
font-size: 18px;
|
|
right: 0px;
|
|
cursor: pointer;
|
|
}
|
|
}
|
|
|
|
.show-pwd {
|
|
position: absolute;
|
|
right: 10px;
|
|
font-size: 16px;
|
|
color: $darkGray;
|
|
cursor: pointer;
|
|
user-select: none;
|
|
}
|
|
}
|
|
|
|
.login-page {
|
|
-webkit-border-radius: 5px;
|
|
border-radius: 5px;
|
|
margin: 130px auto;
|
|
width: 500px;
|
|
padding: 35px 35px 15px;
|
|
border: 1px solid #8c9494;
|
|
box-shadow: 0 0 25px #454646;
|
|
background-color:rgb(247, 255, 255);
|
|
|
|
.loginTab.el-tabs__item {
|
|
width: 180px;
|
|
}
|
|
}
|
|
|
|
label.el-checkbox.rememberme {
|
|
margin: 0px 0px 15px;
|
|
text-align: left;
|
|
}
|
|
</style>
|
|
|