这是基于vue-vben-admin 模板适用于abp Vnext的前端管理项目
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

<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>