Browse Source

注册登录

warrior 8 months ago
parent
commit
3f405bffcc

+ 108 - 3
package-lock.json

@@ -16,7 +16,9 @@
         "@angular/platform-browser": "^18.0.0",
         "@angular/platform-browser-dynamic": "^18.0.0",
         "@angular/router": "^18.0.0",
+        "@types/parse": "^3.0.9",
         "ng-zorro-antd": "^18.0.0",
+        "parse": "^5.1.0",
         "rxjs": "~7.8.0",
         "tslib": "^2.3.0",
         "zone.js": "~0.14.3"
@@ -2431,6 +2433,18 @@
         "node": ">=6.9.0"
       }
     },
+    "node_modules/@babel/runtime-corejs3": {
+      "version": "7.23.2",
+      "resolved": "https://registry.npmmirror.com/@babel/runtime-corejs3/-/runtime-corejs3-7.23.2.tgz",
+      "integrity": "sha512-54cIh74Z1rp4oIjsHjqN+WM4fMyCBYe+LpZ9jWm51CZ1fbH3SkAzQD/3XLoNkjbJ7YEmjobLXyvQrFypRHOrXw==",
+      "dependencies": {
+        "core-js-pure": "^3.30.2",
+        "regenerator-runtime": "^0.14.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
     "node_modules/@babel/template": {
       "version": "7.24.7",
       "resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.24.7.tgz",
@@ -4121,7 +4135,6 @@
       "version": "20.14.6",
       "resolved": "https://registry.npmmirror.com/@types/node/-/node-20.14.6.tgz",
       "integrity": "sha512-JbA0XIJPL1IiNnU7PFxDXyfAwcwVVrOoqyzzyQTyMeVhBzkJVMSkC1LlVsRQ2lpqiY4n6Bb9oCS6lzDKVQxbZw==",
-      "dev": true,
       "dependencies": {
         "undici-types": "~5.26.4"
       }
@@ -4135,6 +4148,14 @@
         "@types/node": "*"
       }
     },
+    "node_modules/@types/parse": {
+      "version": "3.0.9",
+      "resolved": "https://registry.npmmirror.com/@types/parse/-/parse-3.0.9.tgz",
+      "integrity": "sha512-DGTHygc7krgmNAK8h42giwmAofCd9uv2++RD+zw6OmWI7AEnlTYZwEuWsx22SA2CSMQrZW8P2INHLpQbnQFUng==",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
     "node_modules/@types/qs": {
       "version": "6.9.15",
       "resolved": "https://registry.npmmirror.com/@types/qs/-/qs-6.9.15.tgz",
@@ -5536,6 +5557,16 @@
         "url": "https://opencollective.com/core-js"
       }
     },
+    "node_modules/core-js-pure": {
+      "version": "3.37.1",
+      "resolved": "https://registry.npmmirror.com/core-js-pure/-/core-js-pure-3.37.1.tgz",
+      "integrity": "sha512-J/r5JTHSmzTxbiYYrzXg9w1VpqrYt+gexenBE9pugeyhwPZTAEJddyiReJWsLO6uNQ8xJZFbod6XC7KKwatCiA==",
+      "hasInstallScript": true,
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/core-js"
+      }
+    },
     "node_modules/core-util-is": {
       "version": "1.0.3",
       "resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz",
@@ -5713,6 +5744,12 @@
         "node": ">= 8"
       }
     },
+    "node_modules/crypto-js": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmmirror.com/crypto-js/-/crypto-js-4.2.0.tgz",
+      "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
+      "optional": true
+    },
     "node_modules/css-loader": {
       "version": "7.1.1",
       "resolved": "https://registry.npmmirror.com/css-loader/-/css-loader-7.1.1.tgz",
@@ -7275,6 +7312,11 @@
         "postcss": "^8.1.0"
       }
     },
+    "node_modules/idb-keyval": {
+      "version": "6.2.1",
+      "resolved": "https://registry.npmmirror.com/idb-keyval/-/idb-keyval-6.2.1.tgz",
+      "integrity": "sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg=="
+    },
     "node_modules/ieee754": {
       "version": "1.2.1",
       "resolved": "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz",
@@ -9745,6 +9787,25 @@
         "node": ">=6"
       }
     },
+    "node_modules/parse": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmmirror.com/parse/-/parse-5.1.0.tgz",
+      "integrity": "sha512-46gVRe1JHsh21Ht0/Ko6PeMDl6wELLMYxnZPFD6iZm2EWsWnzi2txNGE6PvnIv+G7yOufZIOD0BCZLYOFl3toA==",
+      "dependencies": {
+        "@babel/runtime-corejs3": "7.23.2",
+        "idb-keyval": "6.2.1",
+        "react-native-crypto-js": "1.0.0",
+        "uuid": "9.0.1",
+        "ws": "8.16.0",
+        "xmlhttprequest": "1.8.0"
+      },
+      "engines": {
+        "node": ">=18 <21"
+      },
+      "optionalDependencies": {
+        "crypto-js": "4.2.0"
+      }
+    },
     "node_modules/parse-json": {
       "version": "5.2.0",
       "resolved": "https://registry.npmmirror.com/parse-json/-/parse-json-5.2.0.tgz",
@@ -9778,6 +9839,38 @@
         "node": ">= 0.10"
       }
     },
+    "node_modules/parse/node_modules/uuid": {
+      "version": "9.0.1",
+      "resolved": "https://registry.npmmirror.com/uuid/-/uuid-9.0.1.tgz",
+      "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
+      "funding": [
+        "https://github.com/sponsors/broofa",
+        "https://github.com/sponsors/ctavan"
+      ],
+      "bin": {
+        "uuid": "dist/bin/uuid"
+      }
+    },
+    "node_modules/parse/node_modules/ws": {
+      "version": "8.16.0",
+      "resolved": "https://registry.npmmirror.com/ws/-/ws-8.16.0.tgz",
+      "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==",
+      "engines": {
+        "node": ">=10.0.0"
+      },
+      "peerDependencies": {
+        "bufferutil": "^4.0.1",
+        "utf-8-validate": ">=5.0.2"
+      },
+      "peerDependenciesMeta": {
+        "bufferutil": {
+          "optional": true
+        },
+        "utf-8-validate": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/parse5": {
       "version": "7.1.2",
       "resolved": "https://registry.npmmirror.com/parse5/-/parse5-7.1.2.tgz",
@@ -10312,6 +10405,11 @@
         "node": ">= 0.8"
       }
     },
+    "node_modules/react-native-crypto-js": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/react-native-crypto-js/-/react-native-crypto-js-1.0.0.tgz",
+      "integrity": "sha512-FNbLuG/HAdapQoybeZSoes1PWdOj0w242gb+e1R0hicf3Gyj/Mf8M9NaED2AnXVOX01b2FXomwUiw1xP1K+8sA=="
+    },
     "node_modules/readable-stream": {
       "version": "3.6.2",
       "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz",
@@ -11838,8 +11936,7 @@
     "node_modules/undici-types": {
       "version": "5.26.5",
       "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-5.26.5.tgz",
-      "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
-      "dev": true
+      "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
     },
     "node_modules/unicode-canonical-property-names-ecmascript": {
       "version": "2.0.0",
@@ -13055,6 +13152,14 @@
         }
       }
     },
+    "node_modules/xmlhttprequest": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmmirror.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz",
+      "integrity": "sha512-58Im/U0mlVBLM38NdZjHyhuMtCqa61469k2YP/AaPbvCoV9aQGUpbJBj1QRm2ytRiVQBD/fsw7L2bJGDVQswBA==",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
     "node_modules/y18n": {
       "version": "5.0.8",
       "resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz",

+ 3 - 1
package.json

@@ -18,7 +18,9 @@
     "@angular/platform-browser": "^18.0.0",
     "@angular/platform-browser-dynamic": "^18.0.0",
     "@angular/router": "^18.0.0",
+    "@types/parse": "^3.0.9",
     "ng-zorro-antd": "^18.0.0",
+    "parse": "^5.1.0",
     "rxjs": "~7.8.0",
     "tslib": "^2.3.0",
     "zone.js": "~0.14.3"
@@ -36,4 +38,4 @@
     "karma-jasmine-html-reporter": "~2.1.0",
     "typescript": "~5.4.2"
   }
-}
+}

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

@@ -1,8 +1,12 @@
 import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
 import { provideRouter } from '@angular/router';
-
+import { provideHttpClient } from '@angular/common/http';
 import { routes } from './app.routes';
 
 export const appConfig: ApplicationConfig = {
-  providers: [provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes)]
+  providers: [
+    provideZoneChangeDetection({ eventCoalescing: true }),
+    provideRouter(routes),
+    provideHttpClient(),
+  ],
 };

+ 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 { }

+ 2 - 2
projects/textbook/src/modules/login/login/login.component.html

@@ -60,10 +60,10 @@
       </nz-form-item>
       <div nz-row class="login-form-margin">
         <div nz-col [nzSpan]="12">
-          <a class="login-form-left">立即注册</a>
+          <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

+ 9 - 9
projects/textbook/src/modules/login/login/login.component.ts

@@ -1,6 +1,5 @@
 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,
@@ -8,18 +7,13 @@ import {
   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',
@@ -76,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;
+  }
+
+}