Browse Source

Merge branch 'master' of http://git.fmode.cn:3000/bin/edu-textbook

MetaPunkGames 8 months ago
parent
commit
95816e8fad

+ 1 - 1
package.json

@@ -21,7 +21,7 @@
     "@ionic/angular": "^8.2.2",
     "@types/parse": "^3.0.9",
     "ng-zorro-antd": "^18.0.0",
-    "parse": "latest",
+    "parse": "^5.1.0",
     "rxjs": "~7.8.0",
     "tslib": "^2.3.0",
     "zone.js": "~0.14.3"

+ 1 - 2
projects/textbook/src/app/app.config.ts

@@ -1,7 +1,6 @@
 
 import { ApplicationConfig, provideZoneChangeDetection, importProvidersFrom } from '@angular/core';
 import { provideRouter } from '@angular/router';
-
 import { routes } from './app.routes';
 import { zh_CN, provideNzI18n } from 'ng-zorro-antd/i18n';
 import { registerLocaleData } from '@angular/common';
@@ -14,4 +13,4 @@ import { provideIonicAngular } from '@ionic/angular/standalone';
 registerLocaleData(zh);
 export const appConfig: ApplicationConfig = {
   providers: [provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes), provideNzI18n(zh_CN), importProvidersFrom(FormsModule), provideAnimationsAsync(), provideHttpClient(), provideIonicAngular({})]
-};
+};

+ 22 - 0
projects/textbook/src/modules/login/common.modules.ts

@@ -0,0 +1,22 @@
+import { NgModule } from '@angular/core';
+import { FormsModule } from '@angular/forms';
+import { NzFormModule } from 'ng-zorro-antd/form';
+import { NzIconModule } from 'ng-zorro-antd/icon';
+import { NzButtonModule } from 'ng-zorro-antd/button';
+import { NzModalModule } from 'ng-zorro-antd/modal';
+import { NzTabsModule } from 'ng-zorro-antd/tabs';
+import { NzInputModule } from 'ng-zorro-antd/input';
+import { NzSelectModule } from 'ng-zorro-antd/select';
+@NgModule({
+  exports: [
+    FormsModule,
+    NzFormModule,
+    NzIconModule,
+    NzButtonModule,
+    NzModalModule,
+    NzTabsModule,
+    NzInputModule,
+    NzSelectModule
+  ]
+})
+export class CommonCompModule { }

+ 15 - 17
projects/textbook/src/modules/login/login/login.component.html

@@ -19,58 +19,56 @@
       class="login-form"
       (ngSubmit)="submitForm()"
     >
-      <nz-form-item>
+      <nz-form-item style="margin-bottom: 6px;">
         <nz-form-label [nzSm]="6" [nzXs]="24" nzRequired nzFor="user">用户名</nz-form-label>
-        <nz-form-control nzErrorTip="请输入你的用户名">
-          <nz-input-group nzPrefixIcon="user">
+        <nz-form-control>
+          <nz-input-group>
             <input
               type="text"
               nz-input
               formControlName="userName"
-              placeholder="用户名/手机号"
+              placeholder="请输入用户名"
             />
           </nz-input-group>
         </nz-form-control>
       </nz-form-item>
-      <nz-form-item>
+      <nz-form-item style="margin-bottom: 6px;">
         <nz-form-label [nzSm]="6" [nzXs]="24" nzRequired nzFor="password">密码</nz-form-label>
-        <nz-form-control nzErrorTip="请输入你的密码">
-          <nz-input-group nzPrefixIcon="lock">
+        <nz-form-control>
+          <nz-input-group>
             <input
               type="password"
               nz-input
               formControlName="password"
-              placeholder="密码"
+              placeholder="请输入密码"
             />
           </nz-input-group>
         </nz-form-control>
       </nz-form-item>
-      <nz-form-item>
+      <nz-form-item style="margin-bottom: 6px;">
         <nz-form-label [nzSm]="6" [nzXs]="24" nzRequired nzFor="code">验证码</nz-form-label>
-        <nz-form-control nzErrorTip="请输入验证码">
-          <nz-input-group nzPrefixIcon="lock">
+        <nz-form-control>
+          <nz-input-group>
             <input
               type="text"
               nz-input
               formControlName="code"
-              placeholder="验证码"
+              placeholder="请输入验证码"
             />
           </nz-input-group>
         </nz-form-control>
       </nz-form-item>
       <div nz-row class="login-form-margin">
         <div nz-col [nzSpan]="12">
-          <!-- <label nz-checkbox formControlName="remember">
-            <span>Remember me</span>
-          </label> -->
+          <a class="login-form-left" (click)="goUrl('/user/register')">立即注册</a>
         </div>
         <div nz-col [nzSpan]="12">
-          <a class="login-form-forgot">忘记密码</a>
+          <a class="login-form-forgot" (click)="goUrl('/user/reset_password')">忘记密码</a>
         </div>
       </div>
       <button
         nz-button
-        class="login-form-button login-form-margin"
+        class="login-form-button"
         [nzType]="'primary'"
       >
         登录

+ 13 - 6
projects/textbook/src/modules/login/login/login.component.scss

@@ -25,20 +25,27 @@
   .form {
     width: 300px;
     padding: 10px;
+    box-shadow: rgba(100, 100, 111, 0.2) 0px 7px 29px 0px;
+    .title{
+      text-align: left;
+      margin-bottom: 6px;
+      font-size: 14px;
+      font-weight: 600;
+    }
     .login-form {
       max-width: 300px;
     }
-
-    .login-form-margin {
-      margin-bottom: 16px;
+    .login-form-left{
+      float: left;
     }
-
     .login-form-forgot {
       float: right;
     }
-
+    .login-form-margin{
+      margin-bottom: 8px;
+    }
     .login-form-button {
-      width: 100%;
+      width: 90%;
     }
   }
 }

+ 12 - 10
projects/textbook/src/modules/login/login/login.component.ts

@@ -1,24 +1,19 @@
 import { Component } from '@angular/core';
-import { NzFormModule } from 'ng-zorro-antd/form';
-import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { ReactiveFormsModule } from '@angular/forms';
+import { Router } from '@angular/router';
 import {
   FormControl,
   FormGroup,
   NonNullableFormBuilder,
   Validators,
 } from '@angular/forms';
-import { NzInputModule } from 'ng-zorro-antd/input';
-import { NzButtonModule } from 'ng-zorro-antd/button';
-
+import { CommonCompModule } from '../common.modules'
 @Component({
   selector: 'app-login',
   standalone: true,
   imports: [
-    NzFormModule,
     ReactiveFormsModule,
-    FormsModule,
-    NzInputModule,
-    NzButtonModule,
+    CommonCompModule
   ],
   templateUrl: './login.component.html',
   styleUrl: './login.component.scss',
@@ -57,10 +52,11 @@ export class LoginComponent {
     name: '国家级管理员',
     type: 1,
   };
-  constructor(private fb: NonNullableFormBuilder) {}
+  constructor(private fb: NonNullableFormBuilder, public router: Router) {}
   submitForm(): void {
     if (this.validateForm.valid) {
       console.log('submit', this.validateForm.value);
+      this.router.navigate(["/admin"])
     } else {
       Object.values(this.validateForm.controls).forEach((control) => {
         if (control.invalid) {
@@ -74,4 +70,10 @@ export class LoginComponent {
   onChange(e: string) {
     this.current = e;
   }
+
+  goUrl(path:string){
+    this.router.navigate([path,{
+      // type: this.current.type,
+    }])
+  }
 }

+ 102 - 1
projects/textbook/src/modules/login/register/register.component.html

@@ -1 +1,102 @@
-<p>register works!</p>
+<div class="card">
+  <div class="resgister-title">欢迎注册</div>
+  <!--手机验证码-->
+  <div class="login-card">
+    <nz-input-group nzCompact>
+      <nz-select [ngModel]="'86'">
+        <nz-option [nzLabel]="'+86'" [nzValue]="'86'"></nz-option>
+      </nz-select>
+      <input
+        type="text"
+        nz-input
+        [(ngModel)]="mobile"
+        [nzStatus]="tbookSer.authMobile(mobile) ? '' : 'error'"
+        style="width: 230px"
+        placeholder="请填写手机号"
+      />
+    </nz-input-group>
+
+    <div class="login-input-box local-code">
+      <input
+        nz-input
+        class="code-input"
+        type="text"
+        [(ngModel)]="localCodeNum"
+        maxlength="4"
+        placeholder="验证码"
+        [nzStatus]="localCodeNum.length == 4 ? '' : 'error'"
+      />
+      <!-- <app-canvas-code
+        [canvas_id]="'canvas_register'"
+        [(drawCode)]="drawCode"
+        #codelogin
+      ></app-canvas-code> -->
+    </div>
+    <div class="login-input-box">
+      <nz-input-group nzSearch [nzAddOnAfter]="suffixButton">
+        <input
+          type="text"
+          nz-input
+          placeholder="请填写短信验证码"
+          class="msg-code"
+          placeholder="请填写短信验证码"
+          [(ngModel)]="code"
+          maxlength="8"
+          [nzStatus]="code.length >= 4 ? '' : 'error'"
+        />
+      </nz-input-group>
+      <ng-template #suffixButton>
+        <button
+          nz-button
+          nzType="primary"
+          [value]="buttonText"
+          [disabled]="isCountingdown"
+          (click)="startCountdown()"
+        >
+          {{ buttonText }}
+        </button>
+      </ng-template>
+    </div>
+    <div class="login-input-box">
+      <nz-input-group>
+        <input
+          type="password"
+          nz-input
+          placeholder="请填写密码(至少8位数)"
+          [(ngModel)]="password"
+          [nzStatus]="password.length >= 8 ? '' : 'error'"
+        />
+      </nz-input-group>
+    </div>
+    <div class="login-input-box">
+      <nz-input-group>
+        <input
+          type="password"
+          nz-input
+          placeholder="请确认密码"
+          [(ngModel)]="confirmPassword"
+          [nzStatus]="confirmPassword.length >= 8 ? '' : 'error'"
+        />
+      </nz-input-group>
+    </div>
+    <div class="login-submit-btn">
+      <button
+        nz-button
+        nzType="primary"
+        nzBlock
+        [nzLoading]="isConfirm"
+        (click)="onRegister()"
+      >
+        注册
+      </button>
+    </div>
+  </div>
+  <div class="card-fonter">
+    <a class="fonter-left"></a>
+    <div class="fonter-right">
+      已有账号?<a style="color: #40a9ff" [routerLink]="['/user/login']"
+        >去登录</a
+      >
+    </div>
+  </div>
+</div>

+ 43 - 0
projects/textbook/src/modules/login/register/register.component.scss

@@ -0,0 +1,43 @@
+.card {
+  width: 320px;
+  margin: 10px auto;
+  box-shadow: rgba(100, 100, 111, 0.2) 0px 7px 29px 0px;
+  background-color: white;
+
+  .resgister-title {
+    text-align: center;
+    margin: 10px auto;
+    font-size: 16px;
+    font-weight: 600;
+  }
+  .login-card {
+    padding: 0 10px;
+    .login-input-box {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin-top: 10px;
+      .phone-input {
+        flex: 1;
+      }
+      .code-input {
+        width: 100px;
+      }
+      .msg-code {
+        width: 150px;
+      }
+    }
+    .login-submit-btn {
+      margin: 50px auto 20px;
+    }
+  }
+  .card-fonter {
+    display: flex;
+    justify-content: space-between;
+    padding: 10px;
+    color: #6d6d6d;
+    .fonter-left {
+      color: #6d6d6d;
+    }
+  }
+}

+ 164 - 4
projects/textbook/src/modules/login/register/register.component.ts

@@ -1,12 +1,172 @@
-import { Component } from '@angular/core';
-
+import { Component, OnInit, ViewChild } from "@angular/core";
+import { CommonCompModule } from '../common.modules'
+import { HttpClient } from "@angular/common/http";
+import { NzMessageService } from "ng-zorro-antd/message";
+import { NzModalService } from "ng-zorro-antd/modal";
+import { Router,RouterModule } from "@angular/router";
+import { catchError } from "rxjs/operators";
+import { textbookServer } from "../../../services/textbook";
+import { AuthServr } from "../../../services/auth.service";
+import Parse from "parse";
 @Component({
   selector: 'app-register',
   standalone: true,
-  imports: [],
+  imports: [CommonCompModule,RouterModule],
   templateUrl: './register.component.html',
   styleUrl: './register.component.scss'
 })
-export class RegisterComponent {
+export class RegisterComponent implements OnInit {
+  @ViewChild("codelogin") codelogin: any; //本地校验码绘画
+
+  company: string = "";
+
+  mobile: string = "";
+  password: string = "";
+  confirmPassword: string = "";
+  code: string = ""; //验证码
+  localCodeNum: string = ""; //用户输入验证码
+  drawCode: Array<string> = []; //本地生成验证码(短信验证码登录)
+
+  passwordVisible: boolean = false;
+  buttonText = "获取验证码";
+  isConfirm: boolean = false; //点击注册
+
+  constructor(
+    private http: HttpClient,
+    private msg: NzMessageService,
+    private modal: NzModalService,
+    private router: Router,
+    private loginServr: AuthServr,
+    public tbookSer: textbookServer
+  ) {
+    this.company = this.tbookSer.company;
+    console.log(this.company);
+    console.log(this.router.url);
+  }
+
+  ngOnInit() {}
+
+  codeDown: boolean = false;
+  //添加倒计时开始和结束的判断
+  isCountingdown = false;
+  startCountdown() {
+    let a = /^1[3456789]\d{9}$/;
+    if (!String(this.mobile).match(a)) {
+      this.msg.error("请填写正确手机号");
+      return;
+    }
+    let str = this.drawCode.join("");
+    if (this.localCodeNum.toLowerCase() != str.toLowerCase()) {
+      this.msg.error("验证码不正确");
+      return;
+    }
+    if (this.codeDown) return;
+    this.codeDown = true;
+    let host =
+      (Parse as any).serverURL?.split("parse")?.[0] ||
+      "https://server.fmode.cn/";
+
+    this.http
+      .post(host + "api/apig/message", {
+        company: this.company,
+        mobile: this.mobile,
+      })
+      .pipe(
+        catchError(async (e) => {
+          // 显示报错
+          console.log(e);
+          this.msg.create("error", e.error.mess || "验证码获取失败");
+          this.codeDown = false;
+          this.isCountingdown = false;
+          return;
+        })
+      )
+      .subscribe((res: any) => {
+        console.log(res);
+        this.codelogin.updateDrawCode();
+        if(res){
+          this.msg.success("发送成功");
+          this.isCountingdown = true;
+          this.time();
+        }
+        this.codeDown = false;
+      });
+  }
+  /* 倒计时 */
+  time() {
+    this.isCountingdown = true;
+    this.buttonText = `${this.loginServr.regcountdown}秒`;
+    const timer = setInterval(() => {
+      this.loginServr.regcountdown--;
+      this.buttonText = `${this.loginServr.regcountdown}秒`;
+      if (this.loginServr.regcountdown === 0) {
+        clearInterval(timer);
+        this.buttonText = "重新发送";
+        this.isCountingdown = false;
+      }
+    }, 1000);
+  }
+  onRegister() {
+    if (!this.tbookSer.authMobile(this.mobile)) {
+      this.msg.error("请填写正确的手机号");
+      return;
+    }
+    if (!this.code.trim()) {
+      this.msg.error("请填写短信验证码");
+      return;
+    }
+    if (this.password.trim().length < 8) {
+      this.msg.error("请填写正确的密码");
+      return;
+    }
+    if (this.password != this.confirmPassword) {
+      this.msg.error("密码不一致");
+      return;
+    }
+    if (this.isConfirm) return;
+    this.isConfirm = true
+    let host =
+      (Parse as any).serverURL?.split("parse")?.[0] ||
+      "https://server.fmode.cn/";
 
+    this.http
+      .post(host + "/api/auth/register", {
+        company: this.company,
+        mobile: this.mobile,
+        code: this.code,
+        password: this.password,
+      })
+      .pipe(
+        catchError(async (e) => {
+          // 显示报错
+          console.log(e);
+          this.msg.create("error", "注册失败:" + e.error?.mess);
+          this.isConfirm = false
+          return;
+        })
+      )
+      .subscribe((res: any) => {
+        console.log(res);
+        this.isConfirm = false
+        if(res){
+          this.modal.success({
+            nzTitle: "注册成功",
+            nzContent: "您已成功注册开发者账号,直接登录?",
+            nzCancelText: "取消",
+            nzOnOk: () => {
+              this.loginServr
+              .login(this.mobile, this.password, this.company)
+              .then((data) => {
+                console.log(data);
+                this.router.navigate(["developer/app-list"])
+              })
+              .catch((err) => {
+                console.log(err);
+                this.msg.error(err.message);
+              });
+            }
+          });
+        }
+      });
+  }
 }

+ 55 - 0
projects/textbook/src/services/auth.service.ts

@@ -0,0 +1,55 @@
+import { Injectable } from "@angular/core";
+import Parse from "parse";
+import { Router } from "@angular/router";
+@Injectable({
+  providedIn: "root",
+})
+export class AuthServr {
+  isLoggedIn = false;
+  countdown: number = 60; //登录时验证码倒计时
+  regcountdown: number = 60; //注册验证码倒计时
+  resetcountdown: number = 60; //重置密码验证码倒计时
+
+  redirectUrl: string = '';
+  constructor(public router: Router) {}
+  login(username:string, password:string, company:string) {
+    return new Promise(async (resolve, reject) => {
+      let a = /^1[3456789]\d{9}$/;
+      //如果是手机号登录,获取对应的mobile
+      if (String(username).match(a)) {
+        let query = new Parse.Query("_User");
+        query.equalTo("company", company);
+        query.equalTo("mobile", username);
+        let res = await query.first();
+        if (res?.id) {
+          username = res.get("username");
+        } else {
+          reject({ message: "用户不存在" });
+          return;
+        }
+      }
+      Parse.User.logIn(username, password)
+        .then(async (data:any) => {
+          // await this.developerSer.authDevCompany(); //更新开发者信息
+          console.log(data);
+          resolve(data);
+        })
+        .catch((err:any) => {
+          console.log(err.message);
+          if (err.message.indexOf("Invalid username/password.") != -1) {
+            reject({ message: "用户名或密码不正确" });
+          } else {
+            reject({ message: "用户名或密码不正确" });
+          }
+        });
+    });
+  }
+
+  logout(): void {
+    Parse.User.logOut().then((user) => {
+      // this.developerSer.companyId = ''
+      window.localStorage.clear()
+      this.router.navigate(["/user/login"]);
+    });
+  }
+}

+ 21 - 0
projects/textbook/src/services/textbook.ts

@@ -0,0 +1,21 @@
+import { Injectable } from "@angular/core";
+import Parse from "parse";
+import { HttpClient } from "@angular/common/http";
+
+@Injectable({
+  providedIn: "root",
+})
+export class textbookServer {
+  company: string = localStorage.getItem("company")!;
+  theme: boolean = false; //深色主题模式
+  constructor(private http: HttpClient) {
+  }
+  authMobile(mobile: string): boolean {
+    let a = /^1[3456789]\d{9}$/;
+    if (!String(mobile).match(a)) {
+      return false;
+    }
+    return true;
+  }
+
+}