mirror of
https://gitee.com/dromara/MaxKey.git
synced 2025-12-06 17:08:29 +08:00
367 lines
12 KiB
TypeScript
367 lines
12 KiB
TypeScript
/*
|
||
* Copyright [2022] [MaxKey of copyright http://www.maxkey.top]
|
||
*
|
||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
* you may not use this file except in compliance with the License.
|
||
* You may obtain a copy of the License at
|
||
*
|
||
* http://www.apache.org/licenses/LICENSE-2.0
|
||
*
|
||
* Unless required by applicable law or agreed to in writing, software
|
||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
* See the License for the specific language governing permissions and
|
||
* limitations under the License.
|
||
*/
|
||
|
||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, OnInit, OnDestroy, AfterViewInit, Optional } from '@angular/core';
|
||
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||
import { Router, ActivatedRoute } from '@angular/router';
|
||
import { throwIfAlreadyLoaded } from '@core';
|
||
import { ReuseTabService } from '@delon/abc/reuse-tab';
|
||
import { SettingsService, _HttpClient } from '@delon/theme';
|
||
import { environment } from '@env/environment';
|
||
import { NzSafeAny } from 'ng-zorro-antd/core/types';
|
||
import { NzMessageService } from 'ng-zorro-antd/message';
|
||
import { NzTabChangeEvent } from 'ng-zorro-antd/tabs';
|
||
import { finalize } from 'rxjs/operators';
|
||
|
||
import { AuthnService } from '../../../service/authn.service';
|
||
import { ImageCaptchaService } from '../../../service/image-captcha.service';
|
||
import { SocialsProviderService } from '../../../service/socials-provider.service';
|
||
import { CONSTS } from '../../../shared/consts';
|
||
|
||
import { stringify } from 'querystring';
|
||
|
||
@Component({
|
||
selector: 'passport-login',
|
||
templateUrl: './login.component.html',
|
||
styleUrls: ['./login.component.less'],
|
||
changeDetection: ChangeDetectionStrategy.OnPush
|
||
})
|
||
export class UserLoginComponent implements OnInit, OnDestroy {
|
||
socials: {
|
||
providers: NzSafeAny[];
|
||
qrScan: string;
|
||
} = {
|
||
providers: [],
|
||
qrScan: ''
|
||
};
|
||
|
||
form: FormGroup;
|
||
error = '';
|
||
loginType = 'normal';
|
||
loading = false;
|
||
passwordVisible = false;
|
||
imageCaptcha = '';
|
||
captchaType = '';
|
||
state = '';
|
||
|
||
count = 0;
|
||
interval$: any;
|
||
|
||
constructor(
|
||
fb: FormBuilder,
|
||
private router: Router,
|
||
private settingsService: SettingsService,
|
||
private authnService: AuthnService,
|
||
private socialsProviderService: SocialsProviderService,
|
||
private imageCaptchaService: ImageCaptchaService,
|
||
@Optional()
|
||
@Inject(ReuseTabService)
|
||
private reuseTabService: ReuseTabService,
|
||
private route: ActivatedRoute,
|
||
private msg: NzMessageService,
|
||
private cdr: ChangeDetectorRef
|
||
) {
|
||
this.form = fb.group({
|
||
userName: [null, [Validators.required]],
|
||
password: [null, [Validators.required]],
|
||
captcha: [null, [Validators.required]],
|
||
mobile: [null, [Validators.required, Validators.pattern(/^1\d{10}$/)]],
|
||
otpCaptcha: [null, [Validators.required]],
|
||
remember: [false]
|
||
});
|
||
}
|
||
|
||
ngOnInit(): void {
|
||
//set redirect_uri , is BASE64URL
|
||
if (this.route.snapshot.queryParams[CONSTS.REDIRECT_URI]) {
|
||
this.authnService.setRedirectUri(this.route.snapshot.queryParams[CONSTS.REDIRECT_URI]);
|
||
}
|
||
|
||
//congress login
|
||
if (this.route.snapshot.queryParams[CONSTS.CONGRESS]) {
|
||
this.congressLogin(this.route.snapshot.queryParams[CONSTS.CONGRESS]);
|
||
}
|
||
|
||
//init socials,state
|
||
this.authnService.clear();
|
||
this.authnService
|
||
.get({ remember_me: localStorage.getItem(CONSTS.REMEMBER) })
|
||
.pipe(
|
||
finalize(() => {
|
||
this.loading = false;
|
||
this.cdr.detectChanges();
|
||
})
|
||
)
|
||
.subscribe(res => {
|
||
this.loading = true;
|
||
if (res.code !== 0) {
|
||
this.error = res.msg;
|
||
} else {
|
||
// 清空路由复用信息
|
||
//console.log(res.data);
|
||
//REMEMBER ME
|
||
if (res.data.token) {
|
||
// 清空路由复用信息
|
||
this.reuseTabService.clear();
|
||
// 设置用户Token信息
|
||
this.authnService.auth(res.data);
|
||
this.authnService.navigate({});
|
||
} else {
|
||
this.socials = res.data.socials;
|
||
this.state = res.data.state;
|
||
this.captchaType = res.data.captchaType;
|
||
//init image captcha
|
||
this.imageCaptchaService.captcha({ state: this.state, captcha: this.captchaType }).subscribe(res => {
|
||
this.imageCaptcha = res.data.image;
|
||
this.cdr.detectChanges();
|
||
});
|
||
}
|
||
}
|
||
});
|
||
this.cdr.detectChanges();
|
||
}
|
||
|
||
congressLogin(congress: string) {
|
||
this.authnService
|
||
.congress({
|
||
congress: congress
|
||
})
|
||
.pipe(
|
||
finalize(() => {
|
||
this.loading = false;
|
||
this.cdr.detectChanges();
|
||
})
|
||
)
|
||
.subscribe(res => {
|
||
this.loading = true;
|
||
if (res.code !== 0) {
|
||
this.error = res.msg;
|
||
} else {
|
||
// 清空路由复用信息
|
||
this.reuseTabService.clear();
|
||
// 设置用户Token信息
|
||
this.authnService.auth(res.data);
|
||
this.authnService.navigate({});
|
||
}
|
||
});
|
||
}
|
||
// #region fields
|
||
|
||
get userName(): AbstractControl {
|
||
return this.form.get('userName')!;
|
||
}
|
||
get password(): AbstractControl {
|
||
return this.form.get('password')!;
|
||
}
|
||
get mobile(): AbstractControl {
|
||
return this.form.get('mobile')!;
|
||
}
|
||
get captcha(): AbstractControl {
|
||
return this.form.get('captcha')!;
|
||
}
|
||
|
||
get otpCaptcha(): AbstractControl {
|
||
return this.form.get('otpCaptcha')!;
|
||
}
|
||
|
||
get remember(): AbstractControl {
|
||
return this.form.get('remember')!;
|
||
}
|
||
|
||
// #endregion
|
||
|
||
// #region get captcha
|
||
getImageCaptcha(): void {
|
||
this.imageCaptchaService.captcha({ state: this.state, captcha: this.captchaType }).subscribe(res => {
|
||
this.imageCaptcha = res.data.image;
|
||
this.cdr.detectChanges();
|
||
});
|
||
}
|
||
|
||
//send sms
|
||
sendOtpCode(): void {
|
||
if (this.mobile.invalid) {
|
||
this.mobile.markAsDirty({ onlySelf: true });
|
||
this.mobile.updateValueAndValidity({ onlySelf: true });
|
||
return;
|
||
}
|
||
this.authnService.produceOtp({ mobile: this.mobile.value }).subscribe(res => {
|
||
if (res.code !== 0) {
|
||
this.msg.success(`发送失败`);
|
||
}
|
||
});
|
||
this.count = 59;
|
||
this.interval$ = setInterval(() => {
|
||
this.count -= 1;
|
||
if (this.count <= 0) {
|
||
clearInterval(this.interval$);
|
||
}
|
||
this.cdr.detectChanges();
|
||
}, 1000);
|
||
}
|
||
|
||
// #endregion
|
||
|
||
submit(): void {
|
||
this.error = '';
|
||
if (this.loginType === 'normal') {
|
||
this.userName.markAsDirty();
|
||
this.userName.updateValueAndValidity();
|
||
this.password.markAsDirty();
|
||
this.password.updateValueAndValidity();
|
||
this.captcha.markAsDirty();
|
||
this.captcha.updateValueAndValidity();
|
||
if (this.userName.invalid || this.password.invalid || this.captcha.invalid) {
|
||
return;
|
||
}
|
||
} else {
|
||
this.mobile.markAsDirty();
|
||
this.mobile.updateValueAndValidity();
|
||
this.otpCaptcha.markAsDirty();
|
||
this.otpCaptcha.updateValueAndValidity();
|
||
if (this.mobile.invalid || this.otpCaptcha.invalid) {
|
||
return;
|
||
}
|
||
}
|
||
|
||
localStorage.setItem(CONSTS.REMEMBER, this.form.get(CONSTS.REMEMBER)?.value);
|
||
|
||
this.loading = true;
|
||
this.cdr.detectChanges();
|
||
this.authnService
|
||
.login({
|
||
authType: this.loginType,
|
||
state: this.state,
|
||
username: this.userName.value,
|
||
password: this.password.value,
|
||
captcha: this.captcha.value,
|
||
mobile: this.mobile.value,
|
||
otpCaptcha: this.otpCaptcha.value,
|
||
remeberMe: this.remember.value
|
||
})
|
||
.pipe(
|
||
finalize(() => {
|
||
this.loading = false;
|
||
this.cdr.detectChanges();
|
||
})
|
||
)
|
||
.subscribe(res => {
|
||
this.loading = true;
|
||
if (res.code !== 0) {
|
||
this.error = '登录失败,请重新登录'; //res.msg;
|
||
//this.msg.success(`登录失败,请重新登录!`);
|
||
if (this.loginType === 'normal') {
|
||
this.getImageCaptcha();
|
||
}
|
||
} else {
|
||
// 清空路由复用信息
|
||
this.reuseTabService.clear();
|
||
// 设置用户Token信息
|
||
this.authnService.auth(res.data);
|
||
this.authnService.navigate({});
|
||
}
|
||
this.cdr.detectChanges();
|
||
});
|
||
}
|
||
|
||
// #region social
|
||
socialauth(provider: string): void {
|
||
this.socialsProviderService.authorize(provider).subscribe(res => {
|
||
//console.log(res.data);
|
||
window.location.href = res.data;
|
||
});
|
||
}
|
||
|
||
getQrCode(): void {
|
||
this.authnService.clearUser();
|
||
this.socialsProviderService.scanqrcode(this.socials.qrScan).subscribe(res => {
|
||
if (res.code === 0) {
|
||
if (this.socials.qrScan === 'workweixin') {
|
||
this.qrScanWorkweixin(res.data);
|
||
} else if (this.socials.qrScan === 'dingtalk') {
|
||
this.qrScanDingtalk(res.data);
|
||
} else if (this.socials.qrScan === 'feishu') {
|
||
this.qrScanFeishu(res.data);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
// #endregion
|
||
|
||
ngOnDestroy(): void {
|
||
if (this.interval$) {
|
||
clearInterval(this.interval$);
|
||
}
|
||
}
|
||
|
||
// #region QR Scan for workweixin, dingtalk ,feishu
|
||
qrScanWorkweixin(data: any) {
|
||
//see doc https://developer.work.weixin.qq.com/document/path/91025
|
||
// @ts-ignore
|
||
let wwLogin = new WwLogin({
|
||
id: 'div_qrcodelogin',
|
||
appid: data.clientId,
|
||
agentid: data.agentId,
|
||
redirect_uri: encodeURIComponent(data.redirectUri),
|
||
state: data.state,
|
||
href: 'data:text/css;base64,LmltcG93ZXJCb3ggLnFyY29kZSB7d2lkdGg6IDI1MHB4O30NCi5pbXBvd2VyQm94IC50aXRsZSB7ZGlzcGxheTogbm9uZTt9DQouaW1wb3dlckJveCAuaW5mbyB7d2lkdGg6IDI1MHB4O30NCi5zdGF0dXNfaWNvbiB7ZGlzcGxheTpub25lfQ0KLmltcG93ZXJCb3ggLnN0YXR1cyB7dGV4dC1hbGlnbjogY2VudGVyO30='
|
||
});
|
||
}
|
||
|
||
qrScanFeishu(data: any) {
|
||
//see doc https://open.feishu.cn/document/common-capabilities/sso/web-application-sso/qr-sdk-documentation
|
||
//remove old div
|
||
var qrcodeDiv = document.querySelector('#div_qrcodelogin');
|
||
qrcodeDiv?.childNodes.forEach(function (value, index, array) {
|
||
qrcodeDiv?.removeChild(value);
|
||
});
|
||
// @ts-ignore
|
||
fsredirectUri = `https://passport.feishu.cn/suite/passport/oauth/authorize?client_id=${data.clientId}&redirect_uri=${encodeURIComponent(
|
||
data.redirectUri
|
||
)}&response_type=code&state=${data.state}`;
|
||
// @ts-ignore
|
||
var redirectUri = fsredirectUri;
|
||
// @ts-ignore
|
||
QRLoginObj = QRLogin({
|
||
id: 'div_qrcodelogin',
|
||
goto: redirectUri,
|
||
width: '300',
|
||
height: '300',
|
||
style: 'border: 0;'
|
||
});
|
||
}
|
||
|
||
qrScanDingtalk(data: any) {
|
||
//see doc https://open.dingtalk.com/document/isvapp-server/scan-qr-code-to-log-on-to-third-party-websites
|
||
var url = encodeURIComponent(data.redirectUri);
|
||
var gotodingtalk = encodeURIComponent(
|
||
`https://oapi.dingtalk.com/connect/oauth2/sns_authorize?appid=${data.clientId}&response_type=code&scope=snsapi_login&state=${data.state}&redirect_uri=${url}`
|
||
);
|
||
// @ts-ignore
|
||
ddredirect_uri = `https://oapi.dingtalk.com/connect/oauth2/sns_authorize?appid=${data.clientId}&response_type=code&scope=snsapi_login&state=${data.state}&redirect_uri=${data.redirectUri}`;
|
||
// @ts-ignore
|
||
var obj = DDLogin({
|
||
id: 'div_qrcodelogin', //这里需要你在自己的页面定义一个HTML标签并设置id,例如<div id="login_container"></div>或<span id="login_container"></span>
|
||
goto: gotodingtalk, //请参考注释里的方式
|
||
style: 'border:none;background-color:#FFFFFF;',
|
||
width: '360',
|
||
height: '400'
|
||
});
|
||
}
|
||
// #region QR Scan end
|
||
}
|