登录模块
This commit is contained in:
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "ng-alain-pro",
|
||||
"name": "tms-obc-web",
|
||||
"version": "0.0.0",
|
||||
"description": "Ng-alain business theme, ng-zorro-antd admin panel front-end framework",
|
||||
"description": "运营后台-WEB",
|
||||
"author": "cipchk <cipchk@qq.com>",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@ -24,7 +24,7 @@ import { NzSpinModule } from 'ng-zorro-antd/spin';
|
||||
import { NzSwitchModule } from 'ng-zorro-antd/switch';
|
||||
import { NzTimelineModule } from 'ng-zorro-antd/timeline';
|
||||
import { NzToolTipModule } from 'ng-zorro-antd/tooltip';
|
||||
|
||||
import { NzGridModule } from 'ng-zorro-antd/grid';
|
||||
import { LayoutPassportComponent } from './passport/passport.component';
|
||||
import { PRO_COMPONENTS } from './pro/index';
|
||||
|
||||
@ -56,6 +56,7 @@ const COMPONENTS: Array<Type<any>> = [...PRO_COMPONENTS, LayoutPassportComponent
|
||||
NoticeIconModule,
|
||||
ThemeBtnModule,
|
||||
ScrollbarModule,
|
||||
NzGridModule,
|
||||
NzMessageModule
|
||||
],
|
||||
declarations: COMPONENTS,
|
||||
|
||||
@ -1,13 +1,17 @@
|
||||
<div class="container">
|
||||
<!-- <pro-langs class="langs" btnClass></pro-langs> -->
|
||||
<div class="wrap">
|
||||
<div class="top">
|
||||
<div class="head">
|
||||
<img class="logo" src="./assets/logo-color.svg">
|
||||
<span class="title">ng-alain pro</span>
|
||||
<nz-layout class="layout">
|
||||
<nz-header>
|
||||
<div nz-row>
|
||||
<div nz-col nzSpan="8" style="display: flex;align-items: center;">
|
||||
<img width="95" height="32" src="./assets/images/user/logo.png" [routerLink]="['/']" />
|
||||
</div>
|
||||
<div class="desc">武林中最有影响力的《葵花宝典》;欲练神功,挥刀自宫</div>
|
||||
<div nz-col nzSpan="8" class="text-center">
|
||||
<label class="title">运营管理平台</label>
|
||||
</div>
|
||||
</div>
|
||||
</nz-header>
|
||||
<nz-content class="content">
|
||||
<div class="inner-content">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
</div>
|
||||
</nz-content>
|
||||
</nz-layout>
|
||||
@ -1,74 +1,21 @@
|
||||
@import '~@delon/theme/index';
|
||||
|
||||
:host {
|
||||
::ng-deep {
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100%;
|
||||
background: #f0f2f5;
|
||||
}
|
||||
.langs {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
line-height: 44px;
|
||||
text-align: right;
|
||||
.ant-dropdown-trigger {
|
||||
display: inline-block;
|
||||
}
|
||||
.anticon {
|
||||
margin-top: 24px;
|
||||
margin-right: 24px;
|
||||
font-size: 14px;
|
||||
vertical-align: top;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.wrap {
|
||||
flex: 1;
|
||||
padding: 32px 0;
|
||||
}
|
||||
.ant-form-item {
|
||||
margin-bottom: 24px;
|
||||
.layout {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@media (min-width: @screen-md-min) {
|
||||
.container {
|
||||
background-image: url('https://gw.alipayobjects.com/zos/rmsportal/TVYTbAXWheQpRcWDaDMu.svg');
|
||||
background-repeat: no-repeat;
|
||||
background-position: center 110px;
|
||||
background-size: 100%;
|
||||
}
|
||||
.wrap {
|
||||
padding: 32px 0 24px;
|
||||
}
|
||||
}
|
||||
.top {
|
||||
text-align: center;
|
||||
}
|
||||
.header {
|
||||
height: 44px;
|
||||
line-height: 44px;
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
.logo {
|
||||
height: 44px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
.title {
|
||||
position: relative;
|
||||
color: @heading-color;
|
||||
font-weight: 600;
|
||||
font-size: 33px;
|
||||
font-family: 'Myriad Pro', 'Helvetica Neue', Arial, Helvetica, sans-serif;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.desc {
|
||||
margin-top: 12px;
|
||||
margin-bottom: 40px;
|
||||
color: @text-color-secondary;
|
||||
font-size: @font-size-base;
|
||||
.ant-layout-header {
|
||||
background: #ffffff;
|
||||
box-shadow: 0px 5px 5px #d1d1d1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
color : #1890ff;
|
||||
font-weight: 800;
|
||||
font-size : 28px;
|
||||
text-align : center;
|
||||
}
|
||||
@ -7,21 +7,6 @@ import { DA_SERVICE_TOKEN, ITokenService } from '@delon/auth';
|
||||
styleUrls: ['./passport.component.less']
|
||||
})
|
||||
export class LayoutPassportComponent implements OnInit {
|
||||
links = [
|
||||
{
|
||||
title: '帮助',
|
||||
href: ''
|
||||
},
|
||||
{
|
||||
title: '隐私',
|
||||
href: ''
|
||||
},
|
||||
{
|
||||
title: '条款',
|
||||
href: ''
|
||||
}
|
||||
];
|
||||
|
||||
constructor(@Inject(DA_SERVICE_TOKEN) private tokenService: ITokenService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
||||
@ -1,35 +0,0 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { SocialService } from '@delon/auth';
|
||||
import { SettingsService } from '@delon/theme';
|
||||
|
||||
@Component({
|
||||
selector: 'app-callback',
|
||||
template: ``,
|
||||
providers: [SocialService]
|
||||
})
|
||||
export class CallbackComponent implements OnInit {
|
||||
type = '';
|
||||
|
||||
constructor(private socialService: SocialService, private settingsSrv: SettingsService, private route: ActivatedRoute) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.type = this.route.snapshot.params.type;
|
||||
this.mockModel();
|
||||
}
|
||||
|
||||
private mockModel(): void {
|
||||
const info = {
|
||||
token: '123456789',
|
||||
name: 'cipchk',
|
||||
email: `${this.type}@${this.type}.com`,
|
||||
id: 10000,
|
||||
time: +new Date()
|
||||
};
|
||||
this.settingsSrv.setUser({
|
||||
...this.settingsSrv.user,
|
||||
...info
|
||||
});
|
||||
this.socialService.callback(info);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
<div class="body-box">
|
||||
<div></div>
|
||||
<div>
|
||||
<img class="box-header" [src]="imageUrl" alt="" srcset="" />
|
||||
<div nz-row class="box-content">
|
||||
<div nz-col nzXs="0" nzSm="0" nzMd="0" nzLg="12"
|
||||
[style]="{ 'background-image': 'url(./assets/images/user/login-image.png)', 'background-size': '100%' }"></div>
|
||||
<div nz-col nzXs="22" nzSm="20" nzMd="16" nzLg="12" class="form-box">
|
||||
<h1 class="title">运营管理后台</h1>
|
||||
<nz-tabset (nzSelectChange)="switch($event)">
|
||||
<nz-tab nzTitle="手机号登录">
|
||||
<sf #captchaSF [layout]="'vertical'" [schema]="captchaSchema" [ui]="captchaUI" [button]="'none'">
|
||||
<ng-template sf-template="smsCode" let-me let-ui="ui" let-schema="schema">
|
||||
<nz-input-group nzSearch [nzPrefix]="prefixTemplateMail" [nzSuffix]="suffixTemplateInfo">
|
||||
<input nz-input type="text" placeholder="请输入验证码" [attr.id]="me.id" [disabled]="me.disabled"
|
||||
[attr.disabled]="me.disabled" [nzSize]="ui.size" [ngModel]="me.formProperty.value"
|
||||
(ngModelChange)="me.setValue($event)" />
|
||||
</nz-input-group>
|
||||
<ng-template #prefixTemplateMail>
|
||||
<i nz-icon nzType="mail"></i>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
</sf>
|
||||
</nz-tab>
|
||||
<nz-tab nzTitle="账户密码登录">
|
||||
<sf #accountSF [layout]="'vertical'" [schema]="accountSchema" [ui]="accountUI" [button]="'none'"></sf>
|
||||
</nz-tab>
|
||||
</nz-tabset>
|
||||
<button nz-button type="button" nzType="primary" nzSize="large" (click)="submit()"
|
||||
[nzLoading]="userSrv?.http?.loading" nzBlock style="border-radius: 4px">
|
||||
登录
|
||||
</button>
|
||||
<div class="other">
|
||||
<p>
|
||||
登录即代表您同意 <a target="_blank" [routerLink]="['/passport/agreement']" [queryParams]="{ type: 1 }">《平台服务协议》</a>
|
||||
<a target="_blank" [queryParams]="{ type: 2 }" [routerLink]="['/passport/agreement']">《隐私政策》</a>
|
||||
</p>
|
||||
<div class="other text-right">
|
||||
<a class="forgetPwd" routerLink="/passport/retrieve-password">忘记密码</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<!-- Copyright © 2015-2020 星链380star.com 版权所有 | 粤ICP备16120630号 -->
|
||||
<p [innerHTML]="copyright"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-template #suffixTemplateInfo>
|
||||
<ng-container *ngIf="count < 1; else intervalTemplate">
|
||||
<span class="msg-btn" style="color: #3370ff; cursor: pointer" (click)="getCaptcha()">获取验证码</span>
|
||||
</ng-container>
|
||||
<ng-template #intervalTemplate> 请等待{{ count }}s </ng-template>
|
||||
</ng-template>
|
||||
103
src/app/routes/passport/components/login/login.component.less
Normal file
103
src/app/routes/passport/components/login/login.component.less
Normal file
@ -0,0 +1,103 @@
|
||||
.body-box {
|
||||
display : flex;
|
||||
-webkit-flex-direction: column;
|
||||
-ms-flex-direction : column;
|
||||
flex-direction : column;
|
||||
justify-content : space-between;
|
||||
width : 100%;
|
||||
max-width : 1080px;
|
||||
height : 100%;
|
||||
margin : auto;
|
||||
padding : 50px 0 80px;
|
||||
-webkit-box-orient : vertical;
|
||||
|
||||
.box-header {
|
||||
max-width : 240px;
|
||||
max-height : 48px;
|
||||
margin-bottom: 36px;
|
||||
}
|
||||
|
||||
.box-content {
|
||||
width : 100%;
|
||||
height: 560px;
|
||||
|
||||
.form-box {
|
||||
margin : 0 auto;
|
||||
padding : 40px 88px 40px;
|
||||
background-color: #fff;
|
||||
|
||||
.title {
|
||||
margin-bottom: 30px;
|
||||
color : #1890ff;
|
||||
font-weight : 800;
|
||||
font-size : 32px;
|
||||
text-align : center;
|
||||
}
|
||||
|
||||
.other {
|
||||
margin-top : 24px;
|
||||
line-height: 22px;
|
||||
text-align : left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.box-footer {
|
||||
padding-top: 70px;
|
||||
color : #626262;
|
||||
font-weight: 400;
|
||||
font-size : 14px;
|
||||
text-align : center;
|
||||
}
|
||||
}
|
||||
|
||||
::ng-deep {
|
||||
passport-login {
|
||||
background-color: #f0f4f7;
|
||||
}
|
||||
}
|
||||
|
||||
:host ::ng-deep {
|
||||
|
||||
input::-webkit-outer-spin-button,
|
||||
input::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
/* 火狐 */
|
||||
input[type='number'] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
|
||||
// tabs样式调整
|
||||
.ant-tabs-nav-list {
|
||||
justify-content: space-between;
|
||||
width : 281px;
|
||||
margin : auto;
|
||||
|
||||
.ant-tabs-tab-btn {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.ant-tabs-tab {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-tabs-top>.ant-tabs-nav {
|
||||
margin: 0 0 30px 0;
|
||||
|
||||
.ant-tabs-ink-bar {
|
||||
height: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-tabs-top>.ant-tabs-nav::before {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
nz-input-group {
|
||||
height : 50px;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
224
src/app/routes/passport/components/login/login.component.ts
Normal file
224
src/app/routes/passport/components/login/login.component.ts
Normal file
@ -0,0 +1,224 @@
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, OnDestroy, OnInit, Optional, ViewChild } from '@angular/core';
|
||||
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { Router } from '@angular/router';
|
||||
import { StartupService } from '@core';
|
||||
import { ReuseTabService } from '@delon/abc/reuse-tab';
|
||||
import { DA_SERVICE_TOKEN, ITokenService, SocialOpenType, SocialService } from '@delon/auth';
|
||||
import { SFComponent, SFSchema, SFUISchema, SFStringWidgetSchema } from '@delon/form';
|
||||
import { SettingsService, _HttpClient } from '@delon/theme';
|
||||
import { environment } from '@env/environment';
|
||||
import { NzTabChangeEvent } from 'ng-zorro-antd/tabs';
|
||||
import { interval } from 'rxjs';
|
||||
import { finalize, take } from 'rxjs/operators';
|
||||
import { DunHelper } from 'src/app/shared/components/captcha';
|
||||
import { EAUserService, EACaptchaService, EAValidateService, EAPlatformService } from 'src/app/shared/services';
|
||||
|
||||
@Component({
|
||||
selector: 'passport-login',
|
||||
templateUrl: './login.component.html',
|
||||
styleUrls: ['./login.component.less'],
|
||||
host: {
|
||||
'[class.ant-row]': 'true',
|
||||
'[class.pro-passport]': 'true'
|
||||
}
|
||||
})
|
||||
export class UserLoginComponent implements OnInit, OnDestroy {
|
||||
@ViewChild('accountSF', { static: false })
|
||||
accountSF!: SFComponent;
|
||||
@ViewChild('captchaSF', { static: false })
|
||||
captchaSF!: SFComponent;
|
||||
accountSchema!: SFSchema;
|
||||
accountUI!: SFUISchema;
|
||||
captchaSchema!: SFSchema;
|
||||
captchaUI!: SFUISchema;
|
||||
count = 0;
|
||||
type = 0;
|
||||
// vcode = null;
|
||||
|
||||
imageUrl = './assets/images/user/logo.png';
|
||||
copyright = '';
|
||||
constructor(
|
||||
public userSrv: EAUserService,
|
||||
private captchaSrv: EACaptchaService,
|
||||
private validateSrv: EAValidateService,
|
||||
private dunHelper: DunHelper,
|
||||
private cdr: ChangeDetectorRef,
|
||||
private router: Router
|
||||
) {
|
||||
// this.vcode = this.platformSrv.getOperatorCode();
|
||||
// 加载copyright信息
|
||||
// this.userSrv
|
||||
// .request(this.platformSrv.$api_get_config, {
|
||||
// pageIndex: 1,
|
||||
// pageSize: 999,
|
||||
// })
|
||||
// .subscribe((res: any) => {
|
||||
// this.copyright = res?.records?.find((f: any) => f.configKey === 'website.copyright')?.configValue;
|
||||
// this.imageUrl = res?.records?.find((f: any) => f.configKey === 'platform.saas.logo')?.configValue;
|
||||
// });
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.initAccountSF();
|
||||
this.initCaptchaSF();
|
||||
}
|
||||
|
||||
initAccountSF(): void {
|
||||
this.accountSchema = {
|
||||
properties: {
|
||||
username: {
|
||||
title: '',
|
||||
type: 'string',
|
||||
maxLength: 30,
|
||||
ui: {
|
||||
placeholder: '请输入您的账号',
|
||||
prefixIcon: 'user',
|
||||
size: 'large'
|
||||
} as SFStringWidgetSchema
|
||||
},
|
||||
password: {
|
||||
title: '',
|
||||
type: 'string',
|
||||
ui: {
|
||||
placeholder: '请输入您的密码',
|
||||
prefixIcon: 'lock',
|
||||
type: 'password',
|
||||
size: 'large'
|
||||
} as SFStringWidgetSchema
|
||||
}
|
||||
// sc: {
|
||||
// title: '',
|
||||
// type: 'string',
|
||||
// ui: {
|
||||
// hidden: !!this.vcode,
|
||||
// placeholder: '请输入安全码',
|
||||
// prefixIcon: 'safety',
|
||||
// size: 'large',
|
||||
// errors: {
|
||||
// required: '请输入安全码'
|
||||
// }
|
||||
// } as SFStringWidgetSchema,
|
||||
// default: this.vcode
|
||||
// }
|
||||
},
|
||||
required: ['username', 'password']
|
||||
};
|
||||
this.accountUI = {
|
||||
'*': { spanLabelFixed: 110, grid: { span: 24 } }
|
||||
};
|
||||
}
|
||||
|
||||
initCaptchaSF(): void {
|
||||
this.captchaSchema = {
|
||||
properties: {
|
||||
phone: {
|
||||
title: '',
|
||||
type: 'string',
|
||||
format: 'mobile',
|
||||
maxLength: 11,
|
||||
ui: {
|
||||
placeholder: '请输入您的手机号',
|
||||
prefixIcon: 'mobile',
|
||||
size: 'large',
|
||||
errors: { required: '请输入手机号!', format: '手机号格式错误' }
|
||||
} as SFStringWidgetSchema
|
||||
},
|
||||
smsCode: {
|
||||
title: '',
|
||||
type: 'string',
|
||||
maxLength: 6,
|
||||
ui: {
|
||||
widget: 'custom',
|
||||
size: 'large',
|
||||
errors: { required: '请输入验证码!', maxLength: '验证码错误' }
|
||||
}
|
||||
}
|
||||
// sc: {
|
||||
// title: '',
|
||||
// type: 'string',
|
||||
// ui: {
|
||||
// hidden: !!this.vcode,
|
||||
// placeholder: '请输入安全码',
|
||||
// prefixIcon: 'safety',
|
||||
// size: 'large',
|
||||
// errors: {
|
||||
// required: '请输入安全码'
|
||||
// }
|
||||
// } as SFStringWidgetSchema,
|
||||
// default: this.vcode
|
||||
// }
|
||||
},
|
||||
required: ['phone', 'smsCode']
|
||||
};
|
||||
this.captchaUI = {
|
||||
'*': { spanLabelFixed: 110, grid: { span: 24 } }
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取验证码
|
||||
*/
|
||||
getCaptcha(): void {
|
||||
this.captchaSF.getProperty('/phone')?.updateValueAndValidity();
|
||||
const result = this.validateSrv.validateMobile(this.captchaSF.value.phone);
|
||||
// this.captchaSrv.sendSMSCaptchaByMobile();
|
||||
if (result) {
|
||||
this.captchaSrv.sendSMSCaptchaByMobile(this.captchaSF.value.phone).subscribe(res => {
|
||||
if (res.success && res.data.code === '1') {
|
||||
this.captchaSrv.msgSrv.success('发送验证码成功');
|
||||
this.createInterval();
|
||||
} else if (res.data.code === '503046') {
|
||||
this.dunHelper.popUp(this.captchaSF.value.phone).subscribe(_ => {
|
||||
this.createInterval();
|
||||
this.dunHelper.destory();
|
||||
});
|
||||
} else {
|
||||
this.captchaSrv.msgSrv.warning(res.msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录
|
||||
*/
|
||||
submit(): void {
|
||||
if (this.type === 0) {
|
||||
this.captchaSF.validator({ emitError: true });
|
||||
if (!this.captchaSF.valid) {
|
||||
return;
|
||||
}
|
||||
// this.userSrv.loginByCaptcha(this.captchaSF.value.phone, this.captchaSF.value.smsCode, this.captchaSF.value.sc);
|
||||
} else {
|
||||
this.accountSF.validator({ emitError: true });
|
||||
if (!this.accountSF.valid) {
|
||||
return;
|
||||
// this.userSrv.loginByAccount(this.accountSF.value.username, this.accountSF.value.password, this.accountSF.value.sc);
|
||||
}
|
||||
}
|
||||
this.router.navigateByUrl('/');
|
||||
/* if (!this.accountSF.valid && !this.captchaSF.valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
Object.prototype.hasOwnProperty.call(value, 'username')
|
||||
? this.userSrv.loginByAccount(value.username, value.password)
|
||||
: this.userSrv.loginByCaptcha(value.phone, value.smsCode); */
|
||||
}
|
||||
|
||||
switch(ret: any) {
|
||||
this.type = ret.index;
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {}
|
||||
|
||||
private createInterval() {
|
||||
this.count = 59;
|
||||
interval(1000)
|
||||
.pipe(take(60))
|
||||
.subscribe(x => {
|
||||
this.count = 59 - (x + 1);
|
||||
this.cdr.detectChanges();
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
<div class="page-box">
|
||||
<div style="width: 90%; margin: 5rem auto">
|
||||
<h1 class="mb-md mt-md">
|
||||
{{ agreementContent?.typeName }}
|
||||
</h1>
|
||||
<p class="md">最新版本生效日期:{{ agreementContent?.modifyTime }}</p>
|
||||
<div [innerHTML]="agreementContent?.content"></div>
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,12 @@
|
||||
@import '~@delon/theme/index';
|
||||
|
||||
:host {
|
||||
.page-box {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 50px;
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { OrderAgreementComponent } from './order-agreement.component';
|
||||
|
||||
describe('OrderAgreementComponent', () => {
|
||||
let component: OrderAgreementComponent;
|
||||
let fixture: ComponentFixture<OrderAgreementComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [OrderAgreementComponent],
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(OrderAgreementComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,26 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, Params } from '@angular/router';
|
||||
import { PassportService } from '../../services/passport.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-buyer-components-serve-order-agreement',
|
||||
templateUrl: './order-agreement.component.html',
|
||||
styleUrls: ['./order-agreement.component.less']
|
||||
})
|
||||
export class OrderAgreementComponent implements OnInit {
|
||||
agreementContent: any;
|
||||
type = 2;
|
||||
constructor(private ar: ActivatedRoute, private service: PassportService) {
|
||||
ar.queryParams.subscribe((params: Params) => {
|
||||
this.type = params.type || 2;
|
||||
});
|
||||
}
|
||||
ngOnInit() {
|
||||
this.service.request(this.service.$api_get_agreement, { type: this.type }).subscribe(res => {
|
||||
if (res) {
|
||||
console.log(res);
|
||||
this.agreementContent = res;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,144 @@
|
||||
<div class="main">
|
||||
<page-grid>
|
||||
<nz-steps [nzCurrent]="step" nzLabelPlacement="vertical" class="step-title">
|
||||
<nz-step nzTitle="安全验证"></nz-step>
|
||||
<nz-step nzTitle="重设密码"></nz-step>
|
||||
<nz-step nzTitle="完成"></nz-step>
|
||||
</nz-steps>
|
||||
|
||||
<div class="steps-content">
|
||||
<ng-container *ngIf="step === 0">
|
||||
<div nz-row>
|
||||
<div nz-col nzOffset="7" nzMd="9">
|
||||
<form nz-form [formGroup]="formGroup1" class="myForm">
|
||||
<nz-form-item>
|
||||
<nz-form-label nzSpan="6" nzRequired nzFor="phone">手机号</nz-form-label>
|
||||
<nz-form-control nzSpan="18" nzErrorTip="请输入正确的手机号">
|
||||
<input type="text" nz-input formControlName="phone" maxlength="11" nzSize="large"
|
||||
placeholder="请输入手机号" />
|
||||
</nz-form-control>
|
||||
</nz-form-item>
|
||||
<nz-form-item>
|
||||
<nz-form-label nzSpan="6" nzRequired nzFor="smsVerifyCode">验证码</nz-form-label>
|
||||
<nz-form-control nzSpan="18">
|
||||
<nz-input-group [nzSuffix]="suffixTemplateInfo" nzSize="large">
|
||||
<input type="text" nz-input formControlName="smsVerifyCode" maxlength="6" placeholder="请输入验证码" />
|
||||
</nz-input-group>
|
||||
</nz-form-control>
|
||||
</nz-form-item>
|
||||
<nz-form-item>
|
||||
<nz-form-control nzSpan="6" nzOffset="6">
|
||||
<button class="btn-submit" [disabled]="!formGroup1.valid" [nzLoading]="service.http.loading" nz-button
|
||||
nzBlock="true" nzType="primary" (click)="formSubmit()">
|
||||
下一步
|
||||
</button>
|
||||
</nz-form-control>
|
||||
</nz-form-item>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<!-- <app-captcha #dun [phone]="this.formGroup1.value.phone" (done)="captchaDone($event)"></app-captcha> -->
|
||||
|
||||
<ng-container *ngIf="step === 1">
|
||||
<div nz-row>
|
||||
<div nz-col nzOffset="7" nzMd="17">
|
||||
<form nz-form [formGroup]="formGroup2" class="myForm">
|
||||
<nz-form-item>
|
||||
<nz-form-label nzSpan="4" nzRequired nzFor="passWord">设置新密码</nz-form-label>
|
||||
<nz-form-control nzSpan="8">
|
||||
<nz-input-group [nzSuffix]="passWordType" nzSize="large">
|
||||
<input nz-input [type]="isPassWordHide?'password':'text'" formControlName="passWord"
|
||||
(ngModelChange)="validateConfirmPassword()" placeholder="请输入新密码" />
|
||||
</nz-input-group>
|
||||
<ng-template #passWordType>
|
||||
<i nz-icon [nzType]="isPassWordHide?'eye-invisible':'eye'" (click)="isPassWordHide=!isPassWordHide"
|
||||
nzTheme="outline" style="color: #bdb7b7;font-size: 18px;cursor: pointer;"></i>
|
||||
<!-- <ng-container *ngIf="control.hasError('required')"> 请输入确认密码! </ng-container>
|
||||
<ng-container *ngIf="control.hasError('minLength') || control.hasError('pattern')">
|
||||
8-16个字符,支持字母、数字、符号“_”和“-”,必须包含字母和数字!
|
||||
</ng-container> -->
|
||||
</ng-template>
|
||||
</nz-form-control>
|
||||
<div class="input-tootip">
|
||||
<div *ngIf="formGroup2?.value?.passWord;else NodataTemplate">
|
||||
<i *ngIf="formGroup2?.controls?.passWord?.errors" nz-icon nzType="close-circle" nzTheme="fill"
|
||||
class="valid-icon"></i>
|
||||
<i *ngIf="!formGroup2?.controls?.passWord?.errors" nz-icon nzType="check-circle" nzTheme="fill"
|
||||
class="invalid-icon"></i>
|
||||
</div>
|
||||
<label class="tootip-label"> 8-16个字符,支持字母、数字、符号“_”和“-”,必须包含字母和数字</label>
|
||||
</div>
|
||||
</nz-form-item>
|
||||
<nz-form-item>
|
||||
<nz-form-label nzSpan="4" nzRequired nzFor="passWordTo">重复新密码</nz-form-label>
|
||||
<nz-form-control nzSpan="8" nzDisableAutoTips>
|
||||
<nz-input-group [nzSuffix]="confirmPassWordType" nzSize="large">
|
||||
<input nz-input [type]="isConfirmPassWordHide?'password':'text'" formControlName="passWordTo"
|
||||
placeholder="再次输入密码" />
|
||||
</nz-input-group>
|
||||
<ng-template #confirmPassWordType>
|
||||
<i nz-icon [nzType]="isConfirmPassWordHide?'eye-invisible':'eye'"
|
||||
(click)="isConfirmPassWordHide=!isConfirmPassWordHide" nzTheme="outline"
|
||||
style="color: #bdb7b7;font-size: 18px;cursor: pointer;"></i>
|
||||
<!-- <ng-container *ngIf="control.hasError('required')"> 请输入确认密码! </ng-container>
|
||||
<ng-container *ngIf="control.hasError('passWordTo')"> 两次输入的密码不一致! </ng-container> -->
|
||||
</ng-template>
|
||||
</nz-form-control>
|
||||
<div class="input-tootip">
|
||||
<div *ngIf="formGroup2?.value?.passWordTo;else NodataTemplate">
|
||||
<i *ngIf="formGroup2?.controls?.passWordTo?.errors" nz-icon nzType="close-circle" nzTheme="fill"
|
||||
class="valid-icon"></i>
|
||||
<i *ngIf="!formGroup2?.controls?.passWordTo?.errors" nz-icon nzType="check-circle" nzTheme="fill"
|
||||
class="invalid-icon"></i>
|
||||
</div>
|
||||
<label class="tootip-label">设置新密码与重复新密码保持一致</label>
|
||||
</div>
|
||||
</nz-form-item>
|
||||
<nz-form-item>
|
||||
<nz-form-control nzSpan="6" nzOffset="6">
|
||||
<button [disabled]="!formGroup2.valid" [nzLoading]="service.http.loading" nz-button nzBlock="true"
|
||||
nzType="primary" (click)="formSubmit()">
|
||||
确认
|
||||
</button>
|
||||
</nz-form-control>
|
||||
</nz-form-item>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="step === 2">
|
||||
<result [type]="result ? 'success' : 'error'" [title]="title" [description]="subTitle" style="margin-top: 3rem;margin-bottom: 5rem;">
|
||||
<ng-template #title>
|
||||
<div class="title" style="font-size: 24px">
|
||||
<!-- {{ 'app.register-result.msg' | translate: params }} -->
|
||||
<span *ngIf="result">密码设置成功!</span>
|
||||
<span *ngIf="!result">密码设置失败!</span>
|
||||
</div>
|
||||
</ng-template>
|
||||
<ng-template #subTitle>
|
||||
<div style="font-size: 14px">请牢记您的新密码,3秒后自动跳转至登录页...</div>
|
||||
</ng-template>
|
||||
|
||||
<button routerLink="/passport/login" nz-button nzType="primary">
|
||||
<!-- {{ 'app.register-result.back-home' | translate }} -->
|
||||
立即登录
|
||||
</button>
|
||||
<!-- <button routerLink="/" nz-button nzSize="large">
|
||||
{{ 'app.register-result.back-home' | translate }}
|
||||
</button> -->
|
||||
</result>
|
||||
</ng-container>
|
||||
</div>
|
||||
</page-grid>
|
||||
</div>
|
||||
|
||||
<ng-template #suffixTemplateInfo>
|
||||
<ng-container *ngIf="count < 1; else intervalTemplate">
|
||||
<span class="msg-btn" style="color: #3370ff; cursor: pointer" (click)="getCaptcha()">获取验证码</span>
|
||||
</ng-container>
|
||||
<ng-template #intervalTemplate> 请等待{{ count }}s </ng-template>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #NodataTemplate><label class="dot">●</label></ng-template>
|
||||
@ -0,0 +1,86 @@
|
||||
:host {
|
||||
::ng-deep {
|
||||
.ant-steps {
|
||||
margin: 0 auto;
|
||||
width : 500px;
|
||||
}
|
||||
|
||||
.ant-steps-item-process>.ant-steps-item-container>.ant-steps-item-tail::after {
|
||||
background-color: #999494;
|
||||
}
|
||||
|
||||
.ant-steps-item-wait>.ant-steps-item-container>.ant-steps-item-tail::after {
|
||||
background-color: #999494;
|
||||
}
|
||||
|
||||
.ant-spin-container {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
.ant-form-item-label>label {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.main {
|
||||
max-width: 1200px;
|
||||
margin : 0 auto;
|
||||
overflow : hidden;
|
||||
}
|
||||
|
||||
page-grid {
|
||||
width : 100%;
|
||||
height : 100%;
|
||||
background-color: #fff;
|
||||
margin : 100px auto;
|
||||
|
||||
.myForm {
|
||||
margin-top : 3rem;
|
||||
margin-bottom: 5rem;
|
||||
}
|
||||
|
||||
.steps-content {
|
||||
margin-top : 16px;
|
||||
border : 1px dashed #e9e9e9;
|
||||
border-radius : 6px;
|
||||
background-color: #ffffff;
|
||||
min-height : 250px;
|
||||
// text-align: center;
|
||||
}
|
||||
|
||||
.steps-action {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
button {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.input-tootip {
|
||||
width : 250px;
|
||||
padding-left: 20px;
|
||||
display : flex;
|
||||
display : flex;
|
||||
align-items : center;
|
||||
|
||||
.dot {
|
||||
font-size: 21px;
|
||||
color : #bdb8b8;
|
||||
}
|
||||
|
||||
.valid-icon {
|
||||
color: rgb(217 0 27);
|
||||
}
|
||||
|
||||
.invalid-icon {
|
||||
color: rgb(82 196 26);
|
||||
}
|
||||
|
||||
.tootip-label {
|
||||
margin-left: 8px;
|
||||
font-size : 12px;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,196 @@
|
||||
import { AfterViewInit, ChangeDetectorRef, Component, OnInit, ViewChild } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, ValidatorFn, Validators } from '@angular/forms';
|
||||
import { Router } from '@angular/router';
|
||||
import { interval } from 'rxjs';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { EAUserService } from 'src/app/shared/services/business/user.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-routes-password-retrieve-password',
|
||||
templateUrl: './retrieve-password.component.html',
|
||||
styleUrls: ['./retrieve-password.component.less']
|
||||
})
|
||||
export class UserRetrievePasswordComponent implements OnInit, AfterViewInit {
|
||||
step = 0;
|
||||
count = 0;
|
||||
interval$: any;
|
||||
formGroup1!: FormGroup;
|
||||
formGroup2!: FormGroup;
|
||||
confirmPasswordValidator!: ValidatorFn;
|
||||
result = true;
|
||||
|
||||
isPassWordHide = true;
|
||||
isConfirmPassWordHide = true;
|
||||
|
||||
constructor(private fb: FormBuilder, public service: EAUserService, private router: Router, private cdr: ChangeDetectorRef) {}
|
||||
|
||||
ngAfterViewInit(): void {}
|
||||
|
||||
ngOnInit() {
|
||||
this.initForm();
|
||||
}
|
||||
|
||||
initForm() {
|
||||
this.confirmPasswordValidator = control => {
|
||||
if (!control.value) {
|
||||
return { error: true, required: true };
|
||||
} else if (control.value !== this.formGroup2.controls.passWord.value) {
|
||||
return { passWordTo: true, error: true };
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
||||
this.formGroup1 = this.fb.group({
|
||||
phone: [null, [Validators.required]],
|
||||
smsVerifyCode: [null, [Validators.required]],
|
||||
voucher: [null, [Validators.required]]
|
||||
});
|
||||
|
||||
this.formGroup2 = this.fb.group({
|
||||
passWord: [
|
||||
null,
|
||||
[
|
||||
Validators.required,
|
||||
Validators.maxLength(16),
|
||||
Validators.minLength(8),
|
||||
Validators.pattern('^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z-_]{8,16}$')
|
||||
]
|
||||
],
|
||||
passWordTo: [null, [this.confirmPasswordValidator, Validators.maxLength(16), Validators.minLength(8)]],
|
||||
voucher: [null, [Validators.required]],
|
||||
phone: [null, [Validators.required]]
|
||||
});
|
||||
}
|
||||
|
||||
formSubmit() {
|
||||
switch (this.step) {
|
||||
case 0:
|
||||
this.submitForm1();
|
||||
break;
|
||||
case 1:
|
||||
this.submitForm2();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
submitForm1() {
|
||||
for (const i in this.formGroup1.controls) {
|
||||
if (this.formGroup1.controls[i]) {
|
||||
this.formGroup1.controls[i].markAsDirty();
|
||||
this.formGroup1.controls[i].updateValueAndValidity();
|
||||
}
|
||||
}
|
||||
if (this.formGroup1.value.smsVerifyCode.length !== 6) {
|
||||
this.service.msgSrv.error('请输入6位验证码');
|
||||
return;
|
||||
}
|
||||
if (this.formGroup1.valid) {
|
||||
const param = Object.assign({}, this.formGroup1.value);
|
||||
this.service.http.post(this.service.$forgetPasswordVerifyIdentity, param).subscribe((res: any) => {
|
||||
// console.log(res, 'submitForm1');
|
||||
if (res.success === true) {
|
||||
this.formGroup2.patchValue(
|
||||
{
|
||||
voucher: res.data.voucher,
|
||||
phone: this.formGroup1.value.phone
|
||||
},
|
||||
{ onlySelf: true }
|
||||
);
|
||||
this.step = 1;
|
||||
// clearInterval(this.interval$);
|
||||
} else {
|
||||
this.service.msgSrv.warning(res?.msg || '操作超时,请重新开始找回密码的流程!');
|
||||
// this.refreshPage();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
submitForm2() {
|
||||
for (const i in this.formGroup2.controls) {
|
||||
if ( this.formGroup2.controls[i]) {
|
||||
this.formGroup2.controls[i].markAsDirty();
|
||||
this.formGroup2.controls[i].updateValueAndValidity();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.formGroup2.valid) {
|
||||
this.service.http
|
||||
.post(this.service.$voucherUpdatePassword, this.formGroup2.value)
|
||||
.subscribe((res: { success: any; data: boolean; msg: any }) => {
|
||||
if (res.success && res.data === true) {
|
||||
this.result = true;
|
||||
this.step = 2;
|
||||
setTimeout(() => {
|
||||
//this.service.logout();
|
||||
this.router.navigateByUrl('/passport/login');
|
||||
}, 3000);
|
||||
} else {
|
||||
this.result = false;
|
||||
this.service.msgSrv.warning(res.msg || '密码修改失败!');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getCaptcha() {
|
||||
for (const i in this.formGroup1.controls) {
|
||||
if (true) {
|
||||
this.formGroup1.controls[i].markAsDirty();
|
||||
this.formGroup1.controls[i].updateValueAndValidity();
|
||||
}
|
||||
}
|
||||
if (this.formGroup1.value.phone.length !== 11) {
|
||||
this.service.msgSrv.error('请输入11位手机号');
|
||||
return;
|
||||
}
|
||||
if (this.formGroup1.value.phone) {
|
||||
this.service.http
|
||||
.post(`${this.service.$getAccountSMVerificationCode}`, null, { phoneNumber: this.formGroup1.value.phone })
|
||||
.subscribe((res: any) => {
|
||||
// console.log(res, 'res');
|
||||
if (res.success) {
|
||||
this.formGroup1.patchValue(
|
||||
{
|
||||
voucher: res?.data?.voucher
|
||||
},
|
||||
{ onlySelf: true }
|
||||
);
|
||||
if (res?.data?.code === '1') {
|
||||
this.codeCountDown();
|
||||
this.service.msgSrv.success('验证码已发送到该账号绑定的手机号!');
|
||||
} else if (res?.data?.code === '503046') {
|
||||
// this.dun.popUp();
|
||||
} else {
|
||||
this.service.msgSrv.error('获取验证码失败!');
|
||||
}
|
||||
} else {
|
||||
this.service.msgSrv.error(res.msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/* code倒计时 */
|
||||
codeCountDown() {
|
||||
this.count = 59;
|
||||
interval(1000)
|
||||
.pipe(take(60))
|
||||
.subscribe(x => {
|
||||
this.count = 59 - (x + 1);
|
||||
this.cdr.detectChanges();
|
||||
});
|
||||
}
|
||||
/* 网易盾验证通过 */
|
||||
captchaDone(validate: any) {
|
||||
this.codeCountDown();
|
||||
}
|
||||
|
||||
refreshPage() {
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
validateConfirmPassword(): void {
|
||||
setTimeout(() => this.formGroup2.controls.passWordTo.updateValueAndValidity());
|
||||
}
|
||||
}
|
||||
@ -1,21 +0,0 @@
|
||||
<div class="ant-card width-lg" style="margin: 0 auto">
|
||||
<div class="ant-card-body">
|
||||
<div class="avatar">
|
||||
<nz-avatar [nzSrc]="user.avatar" nzIcon="user" nzSize="large"></nz-avatar>
|
||||
</div>
|
||||
<form nz-form [formGroup]="f" (ngSubmit)="submit()" role="form" class="mt-md">
|
||||
<nz-form-item>
|
||||
<nz-form-control nzErrorTip="请输入密码!">
|
||||
<nz-input-group nzSuffixIcon="lock">
|
||||
<input type="password" nz-input formControlName="password" />
|
||||
</nz-input-group>
|
||||
</nz-form-control>
|
||||
</nz-form-item>
|
||||
<nz-row nzType="flex" nzAlign="middle">
|
||||
<nz-col [nzOffset]="12" [nzSpan]="12" style="text-align: right">
|
||||
<button nz-button [disabled]="!f.valid" nzType="primary">锁屏</button>
|
||||
</nz-col>
|
||||
</nz-row>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@ -1,12 +0,0 @@
|
||||
:host ::ng-deep {
|
||||
.ant-card-body {
|
||||
position: relative;
|
||||
margin-top: 80px;
|
||||
}
|
||||
.avatar {
|
||||
position: absolute;
|
||||
top: -20px;
|
||||
left: 50%;
|
||||
margin-left: -20px;
|
||||
}
|
||||
}
|
||||
@ -1,44 +0,0 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { Router } from '@angular/router';
|
||||
import { DA_SERVICE_TOKEN, ITokenService } from '@delon/auth';
|
||||
import { SettingsService, User } from '@delon/theme';
|
||||
|
||||
@Component({
|
||||
selector: 'passport-lock',
|
||||
templateUrl: './lock.component.html',
|
||||
styleUrls: ['./lock.component.less']
|
||||
})
|
||||
export class UserLockComponent {
|
||||
f: FormGroup;
|
||||
|
||||
get user(): User {
|
||||
return this.settings.user;
|
||||
}
|
||||
|
||||
constructor(
|
||||
fb: FormBuilder,
|
||||
@Inject(DA_SERVICE_TOKEN) private tokenService: ITokenService,
|
||||
private settings: SettingsService,
|
||||
private router: Router
|
||||
) {
|
||||
this.f = fb.group({
|
||||
password: [null, Validators.required]
|
||||
});
|
||||
}
|
||||
|
||||
submit(): void {
|
||||
for (const i in this.f.controls) {
|
||||
this.f.controls[i].markAsDirty();
|
||||
this.f.controls[i].updateValueAndValidity();
|
||||
}
|
||||
if (this.f.valid) {
|
||||
console.log('Valid!');
|
||||
console.log(this.f.value);
|
||||
this.tokenService.set({
|
||||
token: '123'
|
||||
});
|
||||
this.router.navigate(['dashboard']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,77 +0,0 @@
|
||||
<form nz-form [formGroup]="form" (ngSubmit)="submit()" role="form">
|
||||
<nz-tabset [nzAnimated]="false" class="tabs" (nzSelectChange)="switch($event)">
|
||||
<nz-tab nzTitle="账户密码登录">
|
||||
<nz-alert *ngIf="error" [nzType]="'error'" [nzMessage]="error" [nzShowIcon]="true" class="mb-lg"></nz-alert>
|
||||
<nz-form-item>
|
||||
<nz-form-control nzErrorTip="Please enter mobile number, muse be: admin or user">
|
||||
<nz-input-group nzSize="large" nzPrefixIcon="user">
|
||||
<input nz-input formControlName="userName" placeholder="username: admin or user" />
|
||||
</nz-input-group>
|
||||
</nz-form-control>
|
||||
</nz-form-item>
|
||||
<nz-form-item>
|
||||
<nz-form-control nzErrorTip="Please enter password, muse be: ">
|
||||
<nz-input-group nzSize="large" nzPrefixIcon="lock">
|
||||
<input nz-input type="password" formControlName="password" placeholder="password: 随便" />
|
||||
</nz-input-group>
|
||||
</nz-form-control>
|
||||
</nz-form-item>
|
||||
</nz-tab>
|
||||
<nz-tab nzTitle="手机号登录">
|
||||
<nz-form-item>
|
||||
<nz-form-control [nzErrorTip]="mobileErrorTip">
|
||||
<nz-input-group nzSize="large" nzPrefixIcon="user">
|
||||
<input nz-input formControlName="mobile" placeholder="mobile number" />
|
||||
</nz-input-group>
|
||||
<ng-template #mobileErrorTip let-i>
|
||||
<ng-container *ngIf="i.errors.required">
|
||||
请输入手机号!
|
||||
</ng-container>
|
||||
<ng-container *ngIf="i.errors.pattern">
|
||||
手机号格式错误!
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
</nz-form-control>
|
||||
</nz-form-item>
|
||||
<nz-form-item>
|
||||
<nz-form-control nzErrorTip="请输入验证码!">
|
||||
<nz-row [nzGutter]="8">
|
||||
<nz-col [nzSpan]="16">
|
||||
<nz-input-group nzSize="large" nzPrefixIcon="mail">
|
||||
<input nz-input formControlName="captcha" placeholder="captcha" />
|
||||
</nz-input-group>
|
||||
</nz-col>
|
||||
<nz-col [nzSpan]="8">
|
||||
<button type="button" nz-button nzSize="large" (click)="getCaptcha()" [disabled]="count >= 0" nzBlock
|
||||
[nzLoading]="loading">
|
||||
{{ count ? count + 's' : '获取验证码' }}
|
||||
</button>
|
||||
</nz-col>
|
||||
</nz-row>
|
||||
</nz-form-control>
|
||||
</nz-form-item>
|
||||
</nz-tab>
|
||||
</nz-tabset>
|
||||
<nz-form-item>
|
||||
<nz-col [nzSpan]="12">
|
||||
<label nz-checkbox formControlName="remember">自动登录</label>
|
||||
</nz-col>
|
||||
<nz-col [nzSpan]="12" class="text-right">
|
||||
<a class="forgot" routerLink="/passport/register">忘记密码</a>
|
||||
</nz-col>
|
||||
</nz-form-item>
|
||||
<nz-form-item>
|
||||
<button nz-button type="submit" nzType="primary" nzSize="large" [nzLoading]="loading" nzBlock>
|
||||
登录
|
||||
</button>
|
||||
</nz-form-item>
|
||||
</form>
|
||||
<div class="other">
|
||||
其他登录方式
|
||||
<i nz-tooltip nzTooltipTitle="in fact Auth0 via window" (click)="open('auth0', 'window')" nz-icon
|
||||
nzType="alipay-circle" class="icon"></i>
|
||||
<i nz-tooltip nzTooltipTitle="in fact Github via redirect" (click)="open('github')" nz-icon nzType="taobao-circle"
|
||||
class="icon"></i>
|
||||
<i (click)="open('weibo', 'window')" nz-icon nzType="weibo-circle" class="icon"></i>
|
||||
<a class="register" routerLink="/passport/register">注册账户</a>
|
||||
</div>
|
||||
@ -1,53 +0,0 @@
|
||||
@import '~@delon/theme/index';
|
||||
:host {
|
||||
display: block;
|
||||
width: 368px;
|
||||
margin: 0 auto;
|
||||
::ng-deep {
|
||||
.ant-tabs .ant-tabs-bar {
|
||||
margin-bottom: 24px;
|
||||
text-align: center;
|
||||
border-bottom: 0;
|
||||
}
|
||||
.ant-tabs-tab {
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
}
|
||||
.ant-input-affix-wrapper .ant-input:not(:first-child) {
|
||||
padding-left: 4px;
|
||||
}
|
||||
.icon {
|
||||
margin-left: 16px;
|
||||
color: rgba(0, 0, 0, 0.2);
|
||||
font-size: 24px;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
transition: color 0.3s;
|
||||
&:hover {
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
.other {
|
||||
margin-top: 24px;
|
||||
line-height: 22px;
|
||||
text-align: left;
|
||||
nz-tooltip {
|
||||
vertical-align: middle;
|
||||
}
|
||||
.register {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[data-theme='dark'] {
|
||||
:host ::ng-deep {
|
||||
.icon {
|
||||
color: rgba(255, 255, 255, 0.2);
|
||||
&:hover {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,213 +0,0 @@
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, OnDestroy, Optional } from '@angular/core';
|
||||
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { Router } from '@angular/router';
|
||||
import { StartupService } from '@core';
|
||||
import { ReuseTabService } from '@delon/abc/reuse-tab';
|
||||
import { DA_SERVICE_TOKEN, ITokenService, SocialOpenType, SocialService } from '@delon/auth';
|
||||
import { SettingsService, _HttpClient } from '@delon/theme';
|
||||
import { environment } from '@env/environment';
|
||||
import { NzTabChangeEvent } from 'ng-zorro-antd/tabs';
|
||||
import { finalize } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'passport-login',
|
||||
templateUrl: './login.component.html',
|
||||
styleUrls: ['./login.component.less'],
|
||||
providers: [SocialService],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class UserLoginComponent implements OnDestroy {
|
||||
constructor(
|
||||
fb: FormBuilder,
|
||||
private router: Router,
|
||||
private settingsService: SettingsService,
|
||||
private socialService: SocialService,
|
||||
@Optional()
|
||||
@Inject(ReuseTabService)
|
||||
private reuseTabService: ReuseTabService,
|
||||
@Inject(DA_SERVICE_TOKEN) private tokenService: ITokenService,
|
||||
private startupSrv: StartupService,
|
||||
private http: _HttpClient,
|
||||
private cdr: ChangeDetectorRef
|
||||
) {
|
||||
this.form = fb.group({
|
||||
userName: [null, [Validators.required]],
|
||||
password: [null, [Validators.required]],
|
||||
mobile: [null, [Validators.required, Validators.pattern(/^1\d{10}$/)]],
|
||||
captcha: [null, [Validators.required]],
|
||||
remember: [true]
|
||||
});
|
||||
}
|
||||
|
||||
// #region fields
|
||||
|
||||
get userName(): AbstractControl {
|
||||
return this.form.controls.userName;
|
||||
}
|
||||
get password(): AbstractControl {
|
||||
return this.form.controls.password;
|
||||
}
|
||||
get mobile(): AbstractControl {
|
||||
return this.form.controls.mobile;
|
||||
}
|
||||
get captcha(): AbstractControl {
|
||||
return this.form.controls.captcha;
|
||||
}
|
||||
form: FormGroup;
|
||||
error = '';
|
||||
type = 0;
|
||||
loading = false;
|
||||
|
||||
// #region get captcha
|
||||
|
||||
count = 0;
|
||||
interval$: any;
|
||||
|
||||
// #endregion
|
||||
|
||||
switch({ index }: NzTabChangeEvent): void {
|
||||
this.type = index!;
|
||||
}
|
||||
|
||||
getCaptcha(): void {
|
||||
if (this.mobile.invalid) {
|
||||
this.mobile.markAsDirty({ onlySelf: true });
|
||||
this.mobile.updateValueAndValidity({ onlySelf: true });
|
||||
return;
|
||||
}
|
||||
this.count = 59;
|
||||
this.interval$ = setInterval(() => {
|
||||
this.count -= 1;
|
||||
if (this.count <= 0) {
|
||||
clearInterval(this.interval$);
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
submit(): void {
|
||||
this.error = '';
|
||||
if (this.type === 0) {
|
||||
this.userName.markAsDirty();
|
||||
this.userName.updateValueAndValidity();
|
||||
this.password.markAsDirty();
|
||||
this.password.updateValueAndValidity();
|
||||
if (this.userName.invalid || this.password.invalid) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
this.mobile.markAsDirty();
|
||||
this.mobile.updateValueAndValidity();
|
||||
this.captcha.markAsDirty();
|
||||
this.captcha.updateValueAndValidity();
|
||||
if (this.mobile.invalid || this.captcha.invalid) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 默认配置中对所有HTTP请求都会强制 [校验](https://ng-alain.com/auth/getting-started) 用户 Token
|
||||
// 然一般来说登录请求不需要校验,因此可以在请求URL加上:`/login?_allow_anonymous=true` 表示不触发用户 Token 校验
|
||||
this.loading = true;
|
||||
this.cdr.detectChanges();
|
||||
this.befaultLogin();
|
||||
return;
|
||||
this.http
|
||||
.post('/login/account?_allow_anonymous=true', {
|
||||
type: this.type,
|
||||
userName: this.userName.value,
|
||||
password: this.password.value
|
||||
})
|
||||
.pipe(
|
||||
finalize(() => {
|
||||
this.loading = true;
|
||||
this.cdr.detectChanges();
|
||||
})
|
||||
)
|
||||
.subscribe(res => {
|
||||
if (res.msg !== 'ok') {
|
||||
this.error = res.msg;
|
||||
this.cdr.detectChanges();
|
||||
return;
|
||||
}
|
||||
// 清空路由复用信息
|
||||
this.reuseTabService.clear();
|
||||
// 设置用户Token信息
|
||||
// TODO: Mock expired value
|
||||
res.user.expired = +new Date() + 1000 * 60 * 5;
|
||||
this.tokenService.set(res.user);
|
||||
// 重新获取 StartupService 内容,我们始终认为应用信息一般都会受当前用户授权范围而影响
|
||||
this.startupSrv.load().then(() => {
|
||||
let url = this.tokenService.referrer!.url || '/';
|
||||
if (url.includes('/passport')) {
|
||||
url = '/';
|
||||
}
|
||||
this.router.navigateByUrl(url);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
befaultLogin() {
|
||||
// 清空路由复用信息
|
||||
this.reuseTabService.clear();
|
||||
// 设置用户Token信息
|
||||
// TODO: Mock expired value
|
||||
// 重新获取 StartupService 内容,我们始终认为应用信息一般都会受当前用户授权范围而影响
|
||||
this.startupSrv.load().then(() => {
|
||||
let url = this.tokenService.referrer!.url || '/';
|
||||
if (url.includes('/passport')) {
|
||||
url = '/';
|
||||
}
|
||||
this.router.navigateByUrl(url);
|
||||
});
|
||||
}
|
||||
|
||||
// #region social
|
||||
|
||||
open(type: string, openType: SocialOpenType = 'href'): void {
|
||||
let url = ``;
|
||||
let callback = ``;
|
||||
if (environment.production) {
|
||||
callback = `https://ng-alain.github.io/ng-alain/#/passport/callback/${type}`;
|
||||
} else {
|
||||
callback = `http://localhost:4200/#/passport/callback/${type}`;
|
||||
}
|
||||
switch (type) {
|
||||
case 'auth0':
|
||||
url = `//cipchk.auth0.com/login?client=8gcNydIDzGBYxzqV0Vm1CX_RXH-wsWo5&redirect_uri=${decodeURIComponent(callback)}`;
|
||||
break;
|
||||
case 'github':
|
||||
url = `//github.com/login/oauth/authorize?client_id=9d6baae4b04a23fcafa2&response_type=code&redirect_uri=${decodeURIComponent(
|
||||
callback
|
||||
)}`;
|
||||
break;
|
||||
case 'weibo':
|
||||
url = `https://api.weibo.com/oauth2/authorize?client_id=1239507802&response_type=code&redirect_uri=${decodeURIComponent(callback)}`;
|
||||
break;
|
||||
}
|
||||
if (openType === 'window') {
|
||||
this.socialService
|
||||
.login(url, '/', {
|
||||
type: 'window'
|
||||
})
|
||||
.subscribe(res => {
|
||||
if (res) {
|
||||
this.settingsService.setUser(res);
|
||||
this.router.navigateByUrl('/');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.socialService.login(url, '/', {
|
||||
type: 'href'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.interval$) {
|
||||
clearInterval(this.interval$);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,56 +0,0 @@
|
||||
<!-- <pro-langs class="pro-passport__langs"></pro-langs> -->
|
||||
<div class="ant-col-lg-16 pro-passport__bg" style="background-image: url('./assets/tmp/img-big/bg-1.jpeg')">
|
||||
<div class="pro-passport__bg-overlay"></div>
|
||||
<div class="text">
|
||||
<h1>Work with us</h1>
|
||||
<p>Our researchers are embedded in teams across computer science, to discover, invent, and build at the largest
|
||||
scale.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ant-col-lg-8 pro-passport__form">
|
||||
<div class="pro-passport__form-logo"><img src="./assets/logo-color.svg" /></div>
|
||||
<h3 class="pro-passport__form-title">注册</h3>
|
||||
<form nz-form #f="ngForm" nzLayout="vertical" [formGroup]="form" (ngSubmit)="submit()" role="form" se-container>
|
||||
<nz-alert *ngIf="error" [nzType]="'error'" [nzMessage]="error" [nzShowIcon]="true" class="mb-lg"></nz-alert>
|
||||
<se label="手机号" required error="手机号格式错误!">
|
||||
<nz-input-group nzSize="large" [nzAddOnBefore]="addOnBeforeTemplate">
|
||||
<ng-template #addOnBeforeTemplate>
|
||||
<nz-select formControlName="mobilePrefix" style="width: 80px">
|
||||
<nz-option [nzLabel]="'+86'" [nzValue]="'+86'"></nz-option>
|
||||
<nz-option [nzLabel]="'+87'" [nzValue]="'+87'"></nz-option>
|
||||
</nz-select>
|
||||
</ng-template>
|
||||
<input formControlName="mobile" nz-input type="tel" placeholder="Phone number" />
|
||||
</nz-input-group>
|
||||
</se>
|
||||
<se label="请输入验证码!" required error="请输入验证码!">
|
||||
<nz-row [nzGutter]="8">
|
||||
<nz-col [nzSpan]="12">
|
||||
<nz-input-group nzSize="large" nzAddonBeforeIcon="anticon anticon-mail">
|
||||
<input nz-input formControlName="captcha" type="tel" placeholder="Captcha" />
|
||||
</nz-input-group>
|
||||
</nz-col>
|
||||
<nz-col [nzSpan]="12">
|
||||
<button type="button" nz-button nzSize="large" (click)="getCaptcha()" [disabled]="count > 0" nzBlock
|
||||
[nzLoading]="http.loading">
|
||||
{{ count ? count + 's' :'获取验证码' }}
|
||||
</button>
|
||||
</nz-col>
|
||||
</nz-row>
|
||||
</se>
|
||||
<se label="密码" required error="请输入密码!">
|
||||
<input nz-input type="password" nzSize="large" formControlName="password" placeholder="Password" />
|
||||
</se>
|
||||
<se>
|
||||
<div class="flex-center-between">
|
||||
<button nz-button nzType="primary" nzSize="large" type="submit" [disabled]="f.invalid"
|
||||
[nzLoading]="http.loading">
|
||||
注册
|
||||
</button>
|
||||
<a routerLink="/passport/login">
|
||||
使用已有账户登录
|
||||
</a>
|
||||
</div>
|
||||
</se>
|
||||
</form>
|
||||
</div>
|
||||
@ -1,28 +0,0 @@
|
||||
@import '~@delon/theme/index';
|
||||
|
||||
:host ::ng-deep {
|
||||
.pro-passport__bg {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 48px;
|
||||
@media (max-width: @screen-md-max) {
|
||||
display: none !important;
|
||||
}
|
||||
.text {
|
||||
position: relative;
|
||||
padding: 0 64px;
|
||||
color: #fff;
|
||||
h1 {
|
||||
margin-bottom: 24px;
|
||||
color: #fff;
|
||||
font-weight: 900;
|
||||
font-size: 56px;
|
||||
}
|
||||
p {
|
||||
font-size: 22px;
|
||||
line-height: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,78 +0,0 @@
|
||||
import { Component, OnDestroy } from '@angular/core';
|
||||
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { Router } from '@angular/router';
|
||||
import { _HttpClient } from '@delon/theme';
|
||||
import { NzMessageService } from 'ng-zorro-antd/message';
|
||||
|
||||
@Component({
|
||||
selector: 'passport-login2',
|
||||
templateUrl: './login2.component.html',
|
||||
styleUrls: ['./login2.component.less'],
|
||||
host: {
|
||||
'[class.ant-row]': 'true',
|
||||
'[class.pro-passport]': 'true'
|
||||
}
|
||||
})
|
||||
export class UserLogin2Component implements OnDestroy {
|
||||
form: FormGroup;
|
||||
error = '';
|
||||
|
||||
constructor(fb: FormBuilder, private router: Router, private msg: NzMessageService, public http: _HttpClient) {
|
||||
this.form = fb.group({
|
||||
mobilePrefix: ['+86'],
|
||||
mobile: [null, [Validators.required, Validators.pattern(/^1\d{10}$/)]],
|
||||
captcha: [null, [Validators.required]],
|
||||
password: [null, [Validators.required, Validators.minLength(6)]]
|
||||
});
|
||||
}
|
||||
|
||||
// #region fields
|
||||
|
||||
get password(): AbstractControl {
|
||||
return this.form.controls.password;
|
||||
}
|
||||
get mobile(): AbstractControl {
|
||||
return this.form.controls.mobile;
|
||||
}
|
||||
get captcha(): AbstractControl {
|
||||
return this.form.controls.captcha;
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region get captcha
|
||||
|
||||
count = 0;
|
||||
interval$: any;
|
||||
|
||||
getCaptcha(): void {
|
||||
if (this.mobile.invalid) {
|
||||
this.mobile.markAsDirty({ onlySelf: true });
|
||||
this.mobile.updateValueAndValidity({ onlySelf: true });
|
||||
return;
|
||||
}
|
||||
this.count = 59;
|
||||
this.interval$ = setInterval(() => {
|
||||
this.count -= 1;
|
||||
if (this.count <= 0) {
|
||||
clearInterval(this.interval$);
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
submit(): void {
|
||||
this.error = '';
|
||||
const data = this.form.value;
|
||||
this.http.post('/register', data).subscribe(() => {
|
||||
this.router.navigate(['passport', 'register-result'], { queryParams: { email: data.mail } });
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.interval$) {
|
||||
clearInterval(this.interval$);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,51 +0,0 @@
|
||||
<!-- <pro-langs class="pro-passport__langs"></pro-langs> -->
|
||||
<div class="pro-passport__bg width-100" style="background-image: url('./assets/tmp/img-big/bg-2.jpeg')">
|
||||
<div class="pro-passport__bg-overlay"></div>
|
||||
<div class="pro-passport__form">
|
||||
<div class="pro-passport__form-logo"><img src="./assets/logo-color.svg" /></div>
|
||||
<h3 class="pro-passport__form-title">注册</h3>
|
||||
<form nz-form #f="ngForm" nzLayout="vertical" [formGroup]="form" (ngSubmit)="submit()" role="form" se-container>
|
||||
<nz-alert *ngIf="error" [nzType]="'error'" [nzMessage]="error" [nzShowIcon]="true" class="mb-lg"></nz-alert>
|
||||
<se label="手机号" required error="手机号格式错误!">
|
||||
<nz-input-group nzSize="large" [nzAddOnBefore]="addOnBeforeTemplate">
|
||||
<ng-template #addOnBeforeTemplate>
|
||||
<nz-select formControlName="mobilePrefix" style="width: 80px">
|
||||
<nz-option [nzLabel]="'+86'" [nzValue]="'+86'"></nz-option>
|
||||
<nz-option [nzLabel]="'+87'" [nzValue]="'+87'"></nz-option>
|
||||
</nz-select>
|
||||
</ng-template>
|
||||
<input formControlName="mobile" nz-input type="tel" placeholder="Phone number" />
|
||||
</nz-input-group>
|
||||
</se>
|
||||
<se label="验证码" required error="请输入验证码!">
|
||||
<nz-row [nzGutter]="8">
|
||||
<nz-col [nzSpan]="12">
|
||||
<nz-input-group nzSize="large" nzAddonBeforeIcon="anticon anticon-mail">
|
||||
<input nz-input formControlName="captcha" type="tel" placeholder="Captcha" />
|
||||
</nz-input-group>
|
||||
</nz-col>
|
||||
<nz-col [nzSpan]="12">
|
||||
<button type="button" nz-button nzSize="large" (click)="getCaptcha()" [disabled]="count > 0" nzBlock
|
||||
[nzLoading]="http.loading">
|
||||
{{ count ? count + 's' : '获取验证码' }}
|
||||
</button>
|
||||
</nz-col>
|
||||
</nz-row>
|
||||
</se>
|
||||
<se label="密码" required error="请输入密码!">
|
||||
<input nz-input type="password" nzSize="large" formControlName="password" placeholder="Password" />
|
||||
</se>
|
||||
<se>
|
||||
<div class="flex-center-between">
|
||||
<button nz-button nzType="primary" nzSize="large" type="submit" [disabled]="f.invalid"
|
||||
[nzLoading]="http.loading">
|
||||
注册
|
||||
</button>
|
||||
<a routerLink="/passport/login">
|
||||
使用已有账户登录
|
||||
</a>
|
||||
</div>
|
||||
</se>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@ -1,13 +0,0 @@
|
||||
@import '~@delon/theme/index';
|
||||
|
||||
:host ::ng-deep {
|
||||
.pro-passport {
|
||||
&__form {
|
||||
position: relative;
|
||||
max-width: 380px;
|
||||
margin: 48px auto 0 auto;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,78 +0,0 @@
|
||||
import { Component, OnDestroy } from '@angular/core';
|
||||
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { Router } from '@angular/router';
|
||||
import { _HttpClient } from '@delon/theme';
|
||||
import { NzMessageService } from 'ng-zorro-antd/message';
|
||||
|
||||
@Component({
|
||||
selector: 'passport-login3',
|
||||
templateUrl: './login3.component.html',
|
||||
styleUrls: ['./login3.component.less'],
|
||||
host: {
|
||||
'[class.ant-row]': 'true',
|
||||
'[class.pro-passport]': 'true'
|
||||
}
|
||||
})
|
||||
export class UserLogin3Component implements OnDestroy {
|
||||
form: FormGroup;
|
||||
error = '';
|
||||
|
||||
constructor(fb: FormBuilder, private router: Router, private msg: NzMessageService, public http: _HttpClient) {
|
||||
this.form = fb.group({
|
||||
mobilePrefix: ['+86'],
|
||||
mobile: [null, [Validators.required, Validators.pattern(/^1\d{10}$/)]],
|
||||
captcha: [null, [Validators.required]],
|
||||
password: [null, [Validators.required, Validators.minLength(6)]]
|
||||
});
|
||||
}
|
||||
|
||||
// #region fields
|
||||
|
||||
get password(): AbstractControl {
|
||||
return this.form.controls.password;
|
||||
}
|
||||
get mobile(): AbstractControl {
|
||||
return this.form.controls.mobile;
|
||||
}
|
||||
get captcha(): AbstractControl {
|
||||
return this.form.controls.captcha;
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region get captcha
|
||||
|
||||
count = 0;
|
||||
interval$: any;
|
||||
|
||||
getCaptcha(): void {
|
||||
if (this.mobile.invalid) {
|
||||
this.mobile.markAsDirty({ onlySelf: true });
|
||||
this.mobile.updateValueAndValidity({ onlySelf: true });
|
||||
return;
|
||||
}
|
||||
this.count = 59;
|
||||
this.interval$ = setInterval(() => {
|
||||
this.count -= 1;
|
||||
if (this.count <= 0) {
|
||||
clearInterval(this.interval$);
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
submit(): void {
|
||||
this.error = '';
|
||||
const data = this.form.value;
|
||||
this.http.post('/register', data).subscribe(() => {
|
||||
this.router.navigate(['passport', 'register-result'], { queryParams: { email: data.mail } });
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.interval$) {
|
||||
clearInterval(this.interval$);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2,13 +2,9 @@ import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
import { LayoutPassportComponent } from '../../layout/passport/passport.component';
|
||||
import { CallbackComponent } from './callback.component';
|
||||
import { UserLockComponent } from './lock/lock.component';
|
||||
import { UserLoginComponent } from './login/login.component';
|
||||
import { UserLogin2Component } from './login2/login2.component';
|
||||
import { UserLogin3Component } from './login3/login3.component';
|
||||
import { UserRegisterResultComponent } from './register-result/register-result.component';
|
||||
import { UserRegisterComponent } from './register/register.component';
|
||||
import { UserLoginComponent } from './components/login/login.component';
|
||||
import { OrderAgreementComponent } from './components/order-agreement/order-agreement.component';
|
||||
import { UserRetrievePasswordComponent } from './components/retrieve-password/retrieve-password.component';
|
||||
|
||||
const routes: Routes = [
|
||||
// passport
|
||||
@ -17,31 +13,22 @@ const routes: Routes = [
|
||||
component: LayoutPassportComponent,
|
||||
children: [
|
||||
{
|
||||
path: 'login',
|
||||
component: UserLoginComponent,
|
||||
data: { title: '登录' }
|
||||
path: 'agreement',
|
||||
component: OrderAgreementComponent,
|
||||
data: { title: '协议' }
|
||||
},
|
||||
{
|
||||
path: 'register',
|
||||
component: UserRegisterComponent,
|
||||
data: { title: '注册' }
|
||||
},
|
||||
{
|
||||
path: 'register-result',
|
||||
component: UserRegisterResultComponent,
|
||||
data: { title: '注册结果' }
|
||||
},
|
||||
{
|
||||
path: 'lock',
|
||||
component: UserLockComponent,
|
||||
data: { title: '锁屏' }
|
||||
path: 'retrieve-password',
|
||||
component: UserRetrievePasswordComponent,
|
||||
data: { title: '忘记密码' }
|
||||
}
|
||||
]
|
||||
},
|
||||
// 单页不包裹Layout
|
||||
{ path: 'login2', component: UserLogin2Component },
|
||||
{ path: 'login3', component: UserLogin3Component },
|
||||
{ path: 'passport/callback/:type', component: CallbackComponent }
|
||||
{
|
||||
path: 'passport/login',
|
||||
component: UserLoginComponent,
|
||||
data: { title: '登录' }
|
||||
}
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
||||
@ -3,6 +3,8 @@ import { NgModule } from '@angular/core';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { ResultModule } from '@delon/abc/result';
|
||||
import { SEModule } from '@delon/abc/se';
|
||||
import { DelonACLModule } from '@delon/acl';
|
||||
import { DelonFormModule } from '@delon/form';
|
||||
import { AlainThemeModule } from '@delon/theme';
|
||||
import { NzAlertModule } from 'ng-zorro-antd/alert';
|
||||
import { NzAvatarModule } from 'ng-zorro-antd/avatar';
|
||||
@ -15,28 +17,21 @@ import { NzInputModule } from 'ng-zorro-antd/input';
|
||||
import { NzPopoverModule } from 'ng-zorro-antd/popover';
|
||||
import { NzProgressModule } from 'ng-zorro-antd/progress';
|
||||
import { NzSelectModule } from 'ng-zorro-antd/select';
|
||||
import { NzStepsModule } from 'ng-zorro-antd/steps';
|
||||
import { NzTabsModule } from 'ng-zorro-antd/tabs';
|
||||
import { NzToolTipModule } from 'ng-zorro-antd/tooltip';
|
||||
import { ProPageModule } from 'src/app/layout/pro/shared/page';
|
||||
import { UserLoginComponent } from './components/login/login.component';
|
||||
import { OrderAgreementComponent } from './components/order-agreement/order-agreement.component';
|
||||
import { UserRetrievePasswordComponent } from './components/retrieve-password/retrieve-password.component';
|
||||
|
||||
import { CallbackComponent } from './callback.component';
|
||||
import { UserLockComponent } from './lock/lock.component';
|
||||
import { UserLoginComponent } from './login/login.component';
|
||||
import { UserLogin2Component } from './login2/login2.component';
|
||||
import { UserLogin3Component } from './login3/login3.component';
|
||||
import { PassportRoutingModule } from './passport-routing.module';
|
||||
import { UserRegisterResultComponent } from './register-result/register-result.component';
|
||||
import { UserRegisterComponent } from './register/register.component';
|
||||
|
||||
const COMPONENTS = [
|
||||
// passport pages
|
||||
UserLoginComponent,
|
||||
UserRegisterComponent,
|
||||
UserRegisterResultComponent,
|
||||
UserLockComponent,
|
||||
// single pages
|
||||
UserLogin2Component,
|
||||
UserLogin3Component,
|
||||
CallbackComponent
|
||||
UserRetrievePasswordComponent,
|
||||
OrderAgreementComponent
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
@ -60,7 +55,11 @@ const COMPONENTS = [
|
||||
NzProgressModule,
|
||||
NzAvatarModule,
|
||||
SEModule,
|
||||
ResultModule
|
||||
ResultModule,
|
||||
DelonACLModule,
|
||||
DelonFormModule,
|
||||
NzStepsModule,
|
||||
ProPageModule,
|
||||
],
|
||||
declarations: COMPONENTS
|
||||
})
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
<result type="success" [title]="title" description="激活邮件已发送到你的邮箱中,邮件有效期为24小时。请及时登录邮箱,点击邮件中的链接激活帐户。">
|
||||
<ng-template #title>
|
||||
<div class="title" style="font-size: 20px">
|
||||
你的账户:{{params?.email}} 注册成功
|
||||
</div>
|
||||
</ng-template>
|
||||
<button (click)="msg.success('email')" nz-button nzSize="large" [nzType]="'primary'">
|
||||
查看邮箱
|
||||
</button>
|
||||
<button routerLink="/" nz-button nzSize="large">
|
||||
返回首页
|
||||
</button>
|
||||
</result>
|
||||
@ -1,15 +0,0 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { NzMessageService } from 'ng-zorro-antd/message';
|
||||
|
||||
@Component({
|
||||
selector: 'passport-register-result',
|
||||
templateUrl: './register-result.component.html'
|
||||
})
|
||||
export class UserRegisterResultComponent {
|
||||
params = { email: '' };
|
||||
email = '';
|
||||
constructor(route: ActivatedRoute, public msg: NzMessageService) {
|
||||
this.params.email = this.email = route.snapshot.queryParams.email || 'ng-alain@example.com';
|
||||
}
|
||||
}
|
||||
@ -1,89 +0,0 @@
|
||||
<h3>注册</h3>
|
||||
<form nz-form [formGroup]="form" (ngSubmit)="submit()" role="form">
|
||||
<nz-alert *ngIf="error" [nzType]="'error'" [nzMessage]="error" [nzShowIcon]="true" class="mb-lg"></nz-alert>
|
||||
<nz-form-item>
|
||||
<nz-form-control [nzErrorTip]="mailErrorTip">
|
||||
<nz-input-group nzSize="large" nzAddonBeforeIcon="user">
|
||||
<input nz-input formControlName="mail" placeholder="Email" />
|
||||
</nz-input-group>
|
||||
<ng-template #mailErrorTip let-i>
|
||||
<ng-container *ngIf="i.errors?.required">请输入邮箱地址!</ng-container>
|
||||
<ng-container *ngIf="i.errors?.email">邮箱地址格式错误!</ng-container>
|
||||
</ng-template>
|
||||
</nz-form-control>
|
||||
</nz-form-item>
|
||||
<nz-form-item>
|
||||
<nz-form-control nzErrorTip="请输入密码!">
|
||||
<nz-input-group nzSize="large" nzAddonBeforeIcon="lock" nz-popover nzPopoverPlacement="right"
|
||||
nzPopoverTrigger="focus" [(nzPopoverVisible)]="visible" nzPopoverOverlayClassName="register-password-cdk"
|
||||
[nzPopoverOverlayStyle]="{ 'width.px': 240 }" [nzPopoverContent]="pwdCdkTpl">
|
||||
<input nz-input type="password" formControlName="password" placeholder="Password" />
|
||||
</nz-input-group>
|
||||
<ng-template #pwdCdkTpl>
|
||||
<div style="padding: 4px 0">
|
||||
<ng-container [ngSwitch]="status">
|
||||
<div *ngSwitchCase="'ok'" class="success">强度:强</div>
|
||||
<div *ngSwitchCase="'pass'" class="warning">强度:中</div>
|
||||
<div *ngSwitchDefault class="error">强度:太短</div>
|
||||
</ng-container>
|
||||
<div class="progress-{{ status }}">
|
||||
<nz-progress [nzPercent]="progress" [nzStatus]="passwordProgressMap[status]" [nzStrokeWidth]="6"
|
||||
[nzShowInfo]="false"></nz-progress>
|
||||
</div>
|
||||
<p class="mt-sm">请至少输入 6 个字符。请不要使用容易被猜到的密码。</p>
|
||||
</div>
|
||||
</ng-template>
|
||||
</nz-form-control>
|
||||
</nz-form-item>
|
||||
<nz-form-item>
|
||||
<nz-form-control [nzErrorTip]="confirmErrorTip">
|
||||
<nz-input-group nzSize="large" nzAddonBeforeIcon="lock">
|
||||
<input nz-input type="password" formControlName="confirm" placeholder="Confirm Password" />
|
||||
</nz-input-group>
|
||||
<ng-template #confirmErrorTip let-i>
|
||||
<ng-container *ngIf="i.errors?.required">请确认密码!</ng-container>
|
||||
<ng-container *ngIf="i.errors?.matchControl">两次输入的密码不匹配!</ng-container>
|
||||
</ng-template>
|
||||
</nz-form-control>
|
||||
</nz-form-item>
|
||||
<nz-form-item>
|
||||
<nz-form-control [nzErrorTip]="mobileErrorTip">
|
||||
<nz-input-group nzSize="large" [nzAddOnBefore]="addOnBeforeTemplate">
|
||||
<ng-template #addOnBeforeTemplate>
|
||||
<nz-select formControlName="mobilePrefix" style="width: 100px">
|
||||
<nz-option [nzLabel]="'+86'" [nzValue]="'+86'"></nz-option>
|
||||
<nz-option [nzLabel]="'+87'" [nzValue]="'+87'"></nz-option>
|
||||
</nz-select>
|
||||
</ng-template>
|
||||
<input formControlName="mobile" nz-input placeholder="Phone number" />
|
||||
</nz-input-group>
|
||||
<ng-template #mobileErrorTip let-i>
|
||||
<ng-container *ngIf="i.errors?.required">请输入手机号!</ng-container>
|
||||
<ng-container *ngIf="i.errors?.pattern">手机号格式错误!</ng-container>
|
||||
</ng-template>
|
||||
</nz-form-control>
|
||||
</nz-form-item>
|
||||
<nz-form-item>
|
||||
<nz-form-control nzErrorTip="请输入验证码!">
|
||||
<nz-row [nzGutter]="8">
|
||||
<nz-col [nzSpan]="16">
|
||||
<nz-input-group nzSize="large" nzAddonBeforeIcon="mail">
|
||||
<input nz-input formControlName="captcha" placeholder="Captcha" />
|
||||
</nz-input-group>
|
||||
</nz-col>
|
||||
<nz-col [nzSpan]="8">
|
||||
<button type="button" nz-button nzSize="large" (click)="getCaptcha()" [disabled]="count > 0" nzBlock
|
||||
[nzLoading]="loading">
|
||||
{{ count ? count + 's' : '获取验证码' }}
|
||||
</button>
|
||||
</nz-col>
|
||||
</nz-row>
|
||||
</nz-form-control>
|
||||
</nz-form-item>
|
||||
<nz-form-item>
|
||||
<button nz-button nzType="primary" nzSize="large" type="submit" [nzLoading]="loading" class="submit">
|
||||
注册
|
||||
</button>
|
||||
<a class="login" routerLink="/passport/login">使用已有账户登录</a>
|
||||
</nz-form-item>
|
||||
</form>
|
||||
@ -1,42 +0,0 @@
|
||||
@import '~@delon/theme/index';
|
||||
:host {
|
||||
display: block;
|
||||
width: 368px;
|
||||
margin: 0 auto;
|
||||
::ng-deep {
|
||||
h3 {
|
||||
margin-bottom: 20px;
|
||||
font-size: 16px;
|
||||
}
|
||||
.submit {
|
||||
width: 50%;
|
||||
}
|
||||
.login {
|
||||
float: right;
|
||||
line-height: @btn-height-lg;
|
||||
}
|
||||
}
|
||||
}
|
||||
::ng-deep {
|
||||
.register-password-cdk {
|
||||
.success,
|
||||
.warning,
|
||||
.error {
|
||||
transition: color 0.3s;
|
||||
}
|
||||
.success {
|
||||
color: @success-color;
|
||||
}
|
||||
.warning {
|
||||
color: @warning-color;
|
||||
}
|
||||
.error {
|
||||
color: @error-color;
|
||||
}
|
||||
.progress-pass > .progress {
|
||||
.ant-progress-bg {
|
||||
background-color: @warning-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,139 +0,0 @@
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy } from '@angular/core';
|
||||
import { AbstractControl, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
|
||||
import { Router } from '@angular/router';
|
||||
import { _HttpClient } from '@delon/theme';
|
||||
import { MatchControl } from '@delon/util/form';
|
||||
import { NzSafeAny } from 'ng-zorro-antd/core/types';
|
||||
import { finalize } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'passport-register',
|
||||
templateUrl: './register.component.html',
|
||||
styleUrls: ['./register.component.less'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class UserRegisterComponent implements OnDestroy {
|
||||
constructor(fb: FormBuilder, private router: Router, private http: _HttpClient, private cdr: ChangeDetectorRef) {
|
||||
this.form = fb.group(
|
||||
{
|
||||
mail: [null, [Validators.required, Validators.email]],
|
||||
password: [null, [Validators.required, Validators.minLength(6), UserRegisterComponent.checkPassword.bind(this)]],
|
||||
confirm: [null, [Validators.required, Validators.minLength(6)]],
|
||||
mobilePrefix: ['+86'],
|
||||
mobile: [null, [Validators.required, Validators.pattern(/^1\d{10}$/)]],
|
||||
captcha: [null, [Validators.required]]
|
||||
},
|
||||
{
|
||||
validators: MatchControl('password', 'confirm')
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// #region fields
|
||||
|
||||
get mail(): AbstractControl {
|
||||
return this.form.controls.mail;
|
||||
}
|
||||
get password(): AbstractControl {
|
||||
return this.form.controls.password;
|
||||
}
|
||||
get confirm(): AbstractControl {
|
||||
return this.form.controls.confirm;
|
||||
}
|
||||
get mobile(): AbstractControl {
|
||||
return this.form.controls.mobile;
|
||||
}
|
||||
get captcha(): AbstractControl {
|
||||
return this.form.controls.captcha;
|
||||
}
|
||||
form: FormGroup;
|
||||
error = '';
|
||||
type = 0;
|
||||
loading = false;
|
||||
visible = false;
|
||||
status = 'pool';
|
||||
progress = 0;
|
||||
passwordProgressMap: { [key: string]: 'success' | 'normal' | 'exception' } = {
|
||||
ok: 'success',
|
||||
pass: 'normal',
|
||||
pool: 'exception'
|
||||
};
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region get captcha
|
||||
|
||||
count = 0;
|
||||
interval$: any;
|
||||
|
||||
static checkPassword(control: FormControl): NzSafeAny {
|
||||
if (!control) {
|
||||
return null;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const self: any = this;
|
||||
self.visible = !!control.value;
|
||||
if (control.value && control.value.length > 9) {
|
||||
self.status = 'ok';
|
||||
} else if (control.value && control.value.length > 5) {
|
||||
self.status = 'pass';
|
||||
} else {
|
||||
self.status = 'pool';
|
||||
}
|
||||
|
||||
if (self.visible) {
|
||||
self.progress = control.value.length * 10 > 100 ? 100 : control.value.length * 10;
|
||||
}
|
||||
}
|
||||
|
||||
getCaptcha(): void {
|
||||
if (this.mobile.invalid) {
|
||||
this.mobile.markAsDirty({ onlySelf: true });
|
||||
this.mobile.updateValueAndValidity({ onlySelf: true });
|
||||
return;
|
||||
}
|
||||
this.count = 59;
|
||||
this.cdr.detectChanges();
|
||||
this.interval$ = setInterval(() => {
|
||||
this.count -= 1;
|
||||
this.cdr.detectChanges();
|
||||
if (this.count <= 0) {
|
||||
clearInterval(this.interval$);
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
submit(): void {
|
||||
this.error = '';
|
||||
Object.keys(this.form.controls).forEach(key => {
|
||||
this.form.controls[key].markAsDirty();
|
||||
this.form.controls[key].updateValueAndValidity();
|
||||
});
|
||||
if (this.form.invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
const data = this.form.value;
|
||||
this.loading = true;
|
||||
this.cdr.detectChanges();
|
||||
this.http
|
||||
.post('/register?_allow_anonymous=true', data)
|
||||
.pipe(
|
||||
finalize(() => {
|
||||
this.loading = false;
|
||||
this.cdr.detectChanges();
|
||||
})
|
||||
)
|
||||
.subscribe(() => {
|
||||
this.router.navigate(['passport', 'register-result'], { queryParams: { email: data.mail } });
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.interval$) {
|
||||
clearInterval(this.interval$);
|
||||
}
|
||||
}
|
||||
}
|
||||
17
src/app/routes/passport/services/passport.service.ts
Normal file
17
src/app/routes/passport/services/passport.service.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { Injectable, Injector } from '@angular/core';
|
||||
import { _HttpClient } from '@delon/theme';
|
||||
import { NzMessageService } from 'ng-zorro-antd/message';
|
||||
import { BaseService } from 'src/app/shared/services/core/base.service';
|
||||
import { EAFileUtil } from 'src/app/shared/utils/file.util';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class PassportService extends BaseService {
|
||||
// 登录协议,服务订购协议
|
||||
public $api_get_agreement = `/scm/cms/cms/agreement/getAgreement?_allow_anonymous=true`;
|
||||
|
||||
constructor(public injector: Injector) {
|
||||
super(injector);
|
||||
}
|
||||
}
|
||||
BIN
src/assets/images/user/login-image.png
Normal file
BIN
src/assets/images/user/login-image.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 227 KiB |
BIN
src/assets/images/user/logo.png
Normal file
BIN
src/assets/images/user/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.4 KiB |
@ -51,7 +51,8 @@ import {
|
||||
TrophyOutline,
|
||||
UsbOutline,
|
||||
UserOutline,
|
||||
WeiboCircleOutline
|
||||
WeiboCircleOutline,
|
||||
EyeInvisibleOutline
|
||||
} from '@ant-design/icons-angular/icons';
|
||||
|
||||
export const ICONS_AUTO = [
|
||||
@ -102,5 +103,6 @@ export const ICONS_AUTO = [
|
||||
TrophyOutline,
|
||||
UsbOutline,
|
||||
UserOutline,
|
||||
WeiboCircleOutline
|
||||
WeiboCircleOutline,
|
||||
EyeInvisibleOutline
|
||||
];
|
||||
|
||||
@ -7,7 +7,17 @@ import {
|
||||
FolderOutline,
|
||||
InfoOutline,
|
||||
LinkOutline,
|
||||
ProfileOutline
|
||||
ProfileOutline,
|
||||
MobileOutline
|
||||
} from '@ant-design/icons-angular/icons';
|
||||
|
||||
export const ICONS = [InfoOutline, BulbOutline, ProfileOutline, ExceptionOutline, LinkOutline, FolderOutline, FileZipOutline];
|
||||
export const ICONS = [
|
||||
InfoOutline,
|
||||
BulbOutline,
|
||||
ProfileOutline,
|
||||
ExceptionOutline,
|
||||
LinkOutline,
|
||||
FolderOutline,
|
||||
FileZipOutline,
|
||||
MobileOutline
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user