GZP 4 місяців тому
батько
коміт
762cfc16f9
47 змінених файлів з 1892 додано та 0 видалено
  1. 12 0
      src/modules/contacts/ai-chat-page/ai-chat-page.component.html
  2. 104 0
      src/modules/contacts/ai-chat-page/ai-chat-page.component.scss
  3. 21 0
      src/modules/contacts/ai-chat-page/ai-chat-page.component.spec.ts
  4. 117 0
      src/modules/contacts/ai-chat-page/ai-chat-page.component.ts
  5. 95 0
      src/modules/contacts/ai-chat-page/class-chat-completion.ts
  6. 104 0
      src/modules/contacts/my-concern/my-concern.component.html
  7. 11 0
      src/modules/contacts/my-concern/my-concern.component.scss
  8. 21 0
      src/modules/contacts/my-concern/my-concern.component.spec.ts
  9. 129 0
      src/modules/contacts/my-concern/my-concern.component.ts
  10. 20 0
      src/modules/user/guard-auth/auth.guard.ts
  11. 15 0
      src/modules/user/guard-auth/auth.local.guard.ts
  12. 35 0
      src/modules/user/page-info/page-info.component.html
  13. 0 0
      src/modules/user/page-info/page-info.component.scss
  14. 21 0
      src/modules/user/page-info/page-info.component.spec.ts
  15. 31 0
      src/modules/user/page-info/page-info.component.ts
  16. 52 0
      src/modules/user/page-login/page-login.component.html
  17. 10 0
      src/modules/user/page-login/page-login.component.scss
  18. 21 0
      src/modules/user/page-login/page-login.component.spec.ts
  19. 51 0
      src/modules/user/page-login/page-login.component.ts
  20. 36 0
      src/modules/user/page-register/page-register.component.html
  21. 60 0
      src/modules/user/page-register/page-register.component.scss
  22. 21 0
      src/modules/user/page-register/page-register.component.spec.ts
  23. 113 0
      src/modules/user/page-register/page-register.component.ts
  24. 25 0
      src/modules/user/service-user/Chat.service.ts
  25. 41 0
      src/modules/user/service-user/Share.service.ts
  26. 16 0
      src/modules/user/service-user/user.service.spec.ts
  27. 48 0
      src/modules/user/service-user/user.service.ts
  28. 20 0
      src/modules/user2/guard-auth/auth.guard.ts
  29. 15 0
      src/modules/user2/guard-auth/auth.local.guard.ts
  30. 35 0
      src/modules/user2/page-info/page-info.component.html
  31. 0 0
      src/modules/user2/page-info/page-info.component.scss
  32. 21 0
      src/modules/user2/page-info/page-info.component.spec.ts
  33. 31 0
      src/modules/user2/page-info/page-info.component.ts
  34. 52 0
      src/modules/user2/page-login/page-login.component.html
  35. 10 0
      src/modules/user2/page-login/page-login.component.scss
  36. 21 0
      src/modules/user2/page-login/page-login.component.spec.ts
  37. 51 0
      src/modules/user2/page-login/page-login.component.ts
  38. 36 0
      src/modules/user2/page-register/page-register.component.html
  39. 60 0
      src/modules/user2/page-register/page-register.component.scss
  40. 21 0
      src/modules/user2/page-register/page-register.component.spec.ts
  41. 113 0
      src/modules/user2/page-register/page-register.component.ts
  42. 25 0
      src/modules/user2/service-user/Chat.service.ts
  43. 41 0
      src/modules/user2/service-user/Share.service.ts
  44. 16 0
      src/modules/user2/service-user/user.service.spec.ts
  45. 48 0
      src/modules/user2/service-user/user.service.ts
  46. 21 0
      src/modules/user2/user-routing.module.ts
  47. 25 0
      src/modules/user2/user.module.ts

Різницю між файлами не показано, бо вона завелика
+ 12 - 0
src/modules/contacts/ai-chat-page/ai-chat-page.component.html


+ 104 - 0
src/modules/contacts/ai-chat-page/ai-chat-page.component.scss

@@ -0,0 +1,104 @@
+/* ---历史消息--- */
+#service-list .mobile-page pre{
+    white-space: pre-wrap;
+    word-wrap: break-word;
+}
+.mobile-page{
+    margin-bottom: 2.5rem;
+}
+.mobile-page .admin-img, .mobile-page .user-img{
+ width: 45px;
+ height: 45px;
+}
+i.triangle-admin,i.triangle-user{
+ width: 0;
+     height: 0;
+     position: absolute;
+     top: 10px;
+ display: inline-block;
+     border-top: 10px solid transparent;
+     border-bottom: 10px solid transparent;
+}
+.mobile-page i.triangle-admin{
+ left: 4px;
+ border-right: 12px solid rgb(248, 248, 248);
+}
+.mobile-page i.triangle-user{
+ right: 4px;
+     border-left: 12px solid #9EEA6A;
+}
+.mobile-page .admin-group, .mobile-page .user-group{
+ padding: 6px;
+ display: flex;
+ display: -webkit-flex;
+}
+.mobile-page .admin-group{
+ justify-content: flex-start;
+ -webkit-justify-content: flex-start;
+}
+.mobile-page .user-group{
+ justify-content: flex-end;
+ -webkit-justify-content: flex-end;
+}
+.mobile-page .admin-reply, .mobile-page .user-reply{
+ display: inline-block;
+ padding: 13px;
+ border-radius: 4px;
+ background-color: #fff;
+ margin:0 15px 12px;
+ font-size: .8rem;
+ white-space: pre-wrap;
+}
+.mobile-page .admin-reply{
+ box-shadow: 0px 0px 2px #ddd;
+}
+.mobile-page .user-reply{
+ text-align: left;
+ background-color: #9EEA6A;
+ box-shadow: 0px 0px 2px #bbb;
+}
+.mobile-page .user-msg, .mobile-page .admin-msg{
+ width: 75%;
+ position: relative;
+}
+.mobile-page .user-msg{
+ text-align: right;
+}
+/*--- 消息输入框--- */
+.footer {
+    padding: .5rem;
+    position: fixed;
+    bottom: 60px;
+    left: 0;
+    right: 0;
+  }
+  .chat-container {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 10px;
+    background-color: #f5f5f5;
+  }
+  
+  .chat-input {
+    flex-grow: 1;
+    padding: 8px;
+    border: none;
+    border-radius: 5px;
+    font-size: 16px;
+    background-color: #fff;
+  }
+  
+  .send-button {
+    margin-left: 10px;
+    padding: 8px 16px;
+    border: none;
+    border-radius: 5px;
+    font-size: 16px;
+    color: #fff;
+    background-color: #007bff;
+    cursor: pointer;
+  }
+  
+  /* 可以根据需要自定义样式,例如调整颜色、边框等 */
+  

+ 21 - 0
src/modules/contacts/ai-chat-page/ai-chat-page.component.spec.ts

@@ -0,0 +1,21 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { AiChatPageComponent } from './ai-chat-page.component';
+
+describe('AiChatPageComponent', () => {
+  let component: AiChatPageComponent;
+  let fixture: ComponentFixture<AiChatPageComponent>;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      declarations: [AiChatPageComponent]
+    });
+    fixture = TestBed.createComponent(AiChatPageComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 117 - 0
src/modules/contacts/ai-chat-page/ai-chat-page.component.ts

@@ -0,0 +1,117 @@
+import { Component } from '@angular/core';
+import { TestChatCompletion, TestChatMessage,oldMessage } from './class-chat-completion';
+import {SharedService} from 'src/modules/user/service-user/Share.service'
+import * as Parse from "parse"
+(Parse as any).serverURL = "https://web2023.fmode.cn/parse"
+Parse.initialize("dev")
+
+
+async function historyMessage(senderId: string,receiverId:string,historyMessages:Array<oldMessage>=[]){
+  await findMessagesByUserName(receiverId,senderId,historyMessages,"assistant")
+  await findMessagesByUserName(senderId,receiverId,historyMessages,"user")
+  historyMessages.sort((a, b) => Number(a.createdAt) - Number(b.createdAt));
+}
+
+async function findMessagesByUserName(senderId: string,receiverId:string,historyMessages:Array<oldMessage>=[],role:string) {
+  const Gzpmessage = Parse.Object.extend('Gzpmessage');
+  const query = new Parse.Query(Gzpmessage);
+  query.equalTo("senderId", senderId);
+  query.equalTo("receiverId", receiverId);
+  try {
+    const messages = await query.find();
+    for (const message of messages) {
+      historyMessages.push({
+        role:role,
+        content:message.get("messageContent"),
+        createdAt:message.get("createdAt").getTime()
+      }) 
+    }
+  } catch (error) {
+    console.error("查询消息时出错:", error);
+  }
+  return historyMessage
+}
+
+export function sendToParse(value:string,senderId:string,receiverId:string){
+  const Gzpmessage = Parse.Object.extend('Gzpmessage');
+    const message = new Gzpmessage();
+    message.set('senderId', senderId);
+    message.set('receiverId', receiverId);
+    message.set('messageContent', value);
+    message.set('createdAt', new Date());
+    // 设置其他字段...
+
+    // 保存对象到数据库
+    message.save().then(
+      (result: any) => {
+        console.log('对话消息已保存到数据库:', result);
+      },
+      (error: any) => {
+        console.error('保存对话消息时出错:', error);
+      }
+    );
+}
+@Component({
+  selector: 'app-ai-chat-page',
+  templateUrl: './ai-chat-page.component.html',
+  styleUrls: ['./ai-chat-page.component.scss']
+})
+export class AiChatPageComponent {
+
+  oldMessage:boolean=true;
+  title: string ='小助手';
+  userId: string = '';
+  userImg: string = ''
+  public userInput: string = '';
+  messageList: Array<TestChatMessage> = [];
+  historyMessages:Array<oldMessage>=[]
+  completion: TestChatCompletion;
+
+  constructor(private sharedService: SharedService) {
+    this.messageList.push({
+      role:'assistant',
+      content:"您好,我是您的旅行小助手,请问现在有什么可以帮到您的。",
+    }
+    )
+    this.completion = new TestChatCompletion(this.messageList,this.sharedService);
+    this.userId = this.sharedService.getUserId();
+    this.userImg = this.sharedService.getUserImg();
+    historyMessage(this.userId,'1',this.historyMessages)
+    // findMessagesByUserName('Aichat',this.userId,this.messageList,"assistant")
+    // findMessagesByUserName(this.userId,'Aichat',this.messageList,"user")
+  }
+  send(value: string) {
+    if (this.sharedService.pdlogin()){
+      this.messageList.push({
+            role: 'user',
+            content: value,
+          });
+      sendToParse(value,this.userId,"1")
+      this.completion.createCompletionByStream();
+    }
+    else{
+      if(value=='开始'){
+        this.messageList.push({
+          role:'assistant',
+          content:"请问",
+        }
+        ) 
+        this.sharedService.isLogin()
+      }
+      else{
+        this.messageList.push({
+          role:'user',
+          content:value,
+        }
+        )
+        this.messageList.push({
+          role:'assistant',
+          content:"对不起,您还没有登入,暂时无法使用此功能",
+        }
+        ) 
+      }    
+    }
+    this.userInput='';
+  }
+  
+}

+ 95 - 0
src/modules/contacts/ai-chat-page/class-chat-completion.ts

@@ -0,0 +1,95 @@
+import { sendToParse } from './ai-chat-page.component';
+import {SharedService} from 'src/modules/user/service-user/Share.service'
+
+export interface TestChatMessage{
+        role:string
+        content:string
+}
+export interface oldMessage{
+  role:string
+  content:string
+  createdAt:any
+}
+export class TestChatCompletion{
+  userId: string = '';
+    messageList:Array<TestChatMessage>
+    constructor(messageList:Array<TestChatMessage>,private sharedService: SharedService){
+      this.userId = this.sharedService.getUserId();
+      this.messageList = messageList
+    }
+  async createCompletionByStream() {
+
+    let token = localStorage.getItem("token");
+    let bodyJson = {
+      "token": `Bearer ${token}`,
+      "messages": this.messageList,
+      "model": "gpt-3.5-turbo",
+      "temperature": 0.5,
+      "presence_penalty": 0,
+      "frequency_penalty": 0,
+      "top_p": 1,
+      "stream":true
+    };
+  
+    let response = await fetch("https://test.fmode.cn/api/apig/aigc/gpt/v1/chat/completions", {
+      "headers": {
+        "accept": "text/event-stream",
+        "sec-fetch-dest": "empty",
+        "sec-fetch-mode": "cors",
+        "sec-fetch-site": "same-site"
+      },
+      "referrer": "https://ai.fmode.cn/",
+      "referrerPolicy": "strict-origin-when-cross-origin",
+      "body": JSON.stringify(bodyJson),
+      "method": "POST",
+      "mode": "cors",
+      "credentials": "omit"
+    });
+    let messageAiReply = ""
+    let messageIndex = this.messageList.length
+    let reader = response.body?.getReader();
+    if (!reader) {
+      throw new Error("Failed to get the response reader.");
+    }
+  
+    let decoder = new TextDecoder();
+    let buffer = "";
+    while (true) {
+      let { done, value } = await reader.read();
+      if (done) {
+        break;
+      }
+  
+      buffer += decoder.decode(value);
+  
+      // Split the buffer by newlines to get individual messages
+      let messages = buffer.split("\n");
+      // Process each message
+      for (let i = 0; i < messages.length - 1; i++) {
+        let message = messages[i];
+        let dataText = message.replace("data:\ ","")
+        if(dataText.startsWith("{")){
+          try{
+            let dataJson = JSON.parse(dataText)
+            messageAiReply += dataJson?.choices?.[0]?.delta?.content || ""
+            this.messageList[messageIndex] = {
+              role:"assistant",
+              content:messageAiReply,
+            }
+          }catch(err){}
+        }
+        if(dataText.startsWith("[")){
+          this.messageList[messageIndex] = {
+            role:"assistant",
+            content:messageAiReply,
+          }
+          sendToParse(messageAiReply,'1',this.userId)
+          console.log(messageAiReply)
+          messageAiReply = ""
+          // const filteredMessages = this.messageList.filter(message => message.role == "assistant");
+        }
+        buffer = buffer.slice(message.length + 1);
+      }
+    }
+  }
+}

+ 104 - 0
src/modules/contacts/my-concern/my-concern.component.html

@@ -0,0 +1,104 @@
+<ion-header>
+    <ion-toolbar>
+      <ion-title>我的关注</ion-title>
+    </ion-toolbar>
+</ion-header>
+<ion-content>
+  <ion-row>
+    <ion-col><ion-searchbar animated="true" placeholder="搜索用户" [(ngModel)]='userInput'></ion-searchbar></ion-col>
+    <ion-col size="auto"><ion-button type="submit" color="primary" (click)="searchCare()">搜索</ion-button></ion-col>
+  </ion-row>
+  <ion-list>
+    <ion-item *ngIf="!ifSearch">
+      <!-- <ion-avatar slot="start"> -->
+        <ion-card (click)="goAiChatPage()">
+          <img style="width: 100px;"src='assets/aichat.png'/>
+          <ion-label style="text-align: center;">旅行小助手</ion-label>
+        </ion-card>    
+    </ion-item>
+    <ion-item *ngIf="ifSearch">
+      <!-- <ion-avatar slot="start"> -->
+        <ion-card >
+          <img style="width: 100px;"src='{{search.userImg}}'/>
+          <ion-label style="text-align: center;">{{search.username}}</ion-label>
+        </ion-card>
+        <ion-button size="large" style="float: left;" (click)="careThis()">
+          关注<ion-icon slot="icon-only" name="star"></ion-icon>
+        </ion-button>
+    </ion-item>
+    <div *ngIf="!ifSearch">
+      <ion-item *ngFor="let care of cares">
+        <!-- <ion-card *ngIf="care.careUsername == '旅行小助手'" (click)="goAiChatPage()">
+          <img style="width: 100px;"src='assets/aichat.png'/>
+          <ion-label style="text-align: center;">{{care.careUsername}}</ion-label>
+        </ion-card> -->
+        <ion-card>
+          <img style="width: 100px;" src='{{care.careUserImg}}'/>
+          <ion-label style="text-align: center;">{{care.careUsername}}</ion-label>
+          
+        </ion-card> 
+        <ion-button size="large" (click)="noCareThis(care)">
+            <ion-icon slot="icon-only" name="star"></ion-icon>取消关注
+          </ion-button>    
+    </ion-item>
+    </div>
+    
+  </ion-list>
+    
+    <ion-infinite-scroll>
+      <div class="infinite-scroll-content">
+        <svg
+          version="1.1"
+          xmlns="http://www.w3.org/2000/svg"
+          xmlns:xlink="http://www.w3.org/1999/xlink"
+          x="0px"
+          y="0px"
+          viewBox="0 0 100 100"
+          enable-background="new 0 0 100 100"
+          xml:space="preserve"
+        >
+          <circle fill="none" stroke="#1b6dff" stroke-width="4" stroke-miterlimit="10" cx="50" cy="50" r="48" />
+          <line
+            fill="none"
+            stroke-linecap="round"
+            stroke="#1b6dff"
+            stroke-width="4"
+            stroke-miterlimit="10"
+            x1="50"
+            y1="50"
+            x2="85"
+            y2="50.5"
+          >
+            <animateTransform
+              attributeName="transform"
+              dur="2s"
+              type="rotate"
+              from="0 50 50"
+              to="360 50 50"
+              repeatCount="indefinite"
+            />
+          </line>
+          <line
+            fill="none"
+            stroke-linecap="round"
+            stroke="#1b6dff"
+            stroke-width="4"
+            stroke-miterlimit="10"
+            x1="50"
+            y1="50"
+            x2="49.5"
+            y2="74"
+          >
+            <animateTransform
+              attributeName="transform"
+              dur="15s"
+              type="rotate"
+              from="0 50 50"
+              to="360 50 50"
+              repeatCount="indefinite"
+            />
+          </line>
+        </svg>
+      </div>
+    </ion-infinite-scroll>
+  </ion-content>

+ 11 - 0
src/modules/contacts/my-concern/my-concern.component.scss

@@ -0,0 +1,11 @@
+svg {
+    width: 20px;
+    height: 20px;
+    display: inline-block;
+  }
+  
+  .infinite-scroll-content {
+    text-align: center;
+    padding: 20px 0;
+  }
+  

+ 21 - 0
src/modules/contacts/my-concern/my-concern.component.spec.ts

@@ -0,0 +1,21 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { MyConcernComponent } from './my-concern.component';
+
+describe('MyConcernComponent', () => {
+  let component: MyConcernComponent;
+  let fixture: ComponentFixture<MyConcernComponent>;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      declarations: [MyConcernComponent]
+    });
+    fixture = TestBed.createComponent(MyConcernComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 129 - 0
src/modules/contacts/my-concern/my-concern.component.ts

@@ -0,0 +1,129 @@
+import { Component, OnInit } from '@angular/core';
+import { AlertController } from '@ionic/angular';
+import {SharedService} from 'src/modules/user/service-user/Share.service'
+import { Router } from '@angular/router';
+import * as Parse from "parse"
+(Parse as any).serverURL = "https://web2023.fmode.cn/parse"
+Parse.initialize("dev")
+
+@Component({
+  selector: 'app-my-concern',
+  templateUrl: './my-concern.component.html',
+  styleUrls: ['./my-concern.component.scss']
+})
+export class MyConcernComponent implements OnInit {
+  public userInput:string='';
+  cares :any = [];
+  search:any={
+    userId:'',
+    usename:'',
+    userImg:''
+  };
+  careUsername:any=''
+  public ifSearch:boolean=false;
+  userId:any=''
+  ngOnInit() {
+    this.findMyCare()
+  }
+  constructor(private sharedService: SharedService,private router:Router,private alertCtrl:AlertController){
+    
+    this.userId=this.sharedService.getUserId()
+    // findMyCare(this.userId,this.cares)
+    // console.log(this.cares)
+  }
+  goAiChatPage(){
+    this.router.navigate(["/pocket-local/social-interactions/ai-chat-page"],{
+    })
+  }
+  goChatPage(spot:Parse.Object){
+    this.router.navigate(["/pocket-local/social-interactions/chat-page"],{
+      queryParams:spot
+    })
+  }
+  async findMyCare() {
+    const GzpCare = Parse.Object.extend('GzpCare');
+    const query = new Parse.Query(GzpCare);
+    query.equalTo("userId", this.userId);
+    try {
+      const Cares = await query.find();
+      for (const care of Cares) {
+        this.cares.push({
+          careUserId:care.get("careUserId"),
+          careUsername:care.get('careUsername'),
+          careUserImg:"http://q1.qlogo.cn/g?b=qq&nk="+care.get("careUserId")+"&s=100"
+        }) 
+      }
+    } catch (error) {
+      console.error("查询消息时出错:", error);
+    }
+  }
+
+  async searchCare(){
+    const GzpUser = Parse.Object.extend('GzpUser');
+    const query = new Parse.Query(GzpUser);
+    query.equalTo("userId", this.userInput);
+    try{
+      const Search = await query.find();
+      this.careUsername=Search[0].get("username"),
+      this.search={
+        userId:Search[0].get("userId"),
+        username:Search[0].get("username"),
+        userImg:"http://q1.qlogo.cn/g?b=qq&nk="+Search[0].get("userId")+"&s=100",
+      }
+      this.ifSearch=true
+    }catch(error){
+      const alert =  await this.alertCtrl.create({
+        header: '查询错误',
+        subHeader: '没有找到该用户',
+        buttons: ['好的'],
+      });
+      await alert.present();
+    }
+    console.log(this.search)
+  }
+  backNoSearvh(){
+    this.ifSearch=false
+  }
+  async careThis(){
+    if(this.userId!=null){
+      const GzpCare = Parse.Object.extend('GzpCare');
+      const carethis = new GzpCare();
+      carethis.set("userId",this.userId);
+      carethis.set("careUserId",this.userInput);
+      carethis.set("careUsername",this.careUsername);
+      carethis.save();
+    }
+    const alert =  await this.alertCtrl.create({
+      header: '添加成功',
+      subHeader: '该用户已成为你的关注用户',
+      buttons: ['好的'],
+    });
+    await alert.present();
+    this.ifSearch=false
+    this.cares=[]
+    this.findMyCare()
+  }
+  async noCareThis(care: any) {
+    if (this.userId != null) {
+      const GzpCare = Parse.Object.extend('GzpCare');
+      const query = new Parse.Query(GzpCare);
+      query.equalTo("userId", this.userId);
+      query.equalTo("careUserId", care.careUserId);
+      query.equalTo("careUsername", care.careUsername);
+      const noCare = await query.find();
+      for (const obj of noCare) {
+        await obj.destroy();
+      }
+      const alert =  await this.alertCtrl.create({
+        header: '取关成功',
+        subHeader: '该用户已不是你的关注用户',
+        buttons: ['好的'],
+      });
+      await alert.present();
+      this.cares=[]
+      this.findMyCare()
+    }
+  }
+  
+}
+

+ 20 - 0
src/modules/user/guard-auth/auth.guard.ts

@@ -0,0 +1,20 @@
+import { CanActivateFn } from '@angular/router';
+import * as Parse from "parse";
+
+export const authGuard: CanActivateFn = (route, state) => {
+  // 检查当前本地存储中,是否有用户验证信息
+  let userAuth = Parse.User.current();
+  if(userAuth?.id){
+    return true;
+  }else{
+    // 暂时存储登陆前用户所在页面
+    let REDIRECT_URL = location.pathname;
+    localStorage.setItem("REDIRECT_URL",REDIRECT_URL);
+    if (location.pathname?.startsWith("/s1234567")){
+        location.href = "/s1234567/user/login"
+    }else{
+        location.href = "/user/login"
+    }
+    return false;
+  }
+};

+ 15 - 0
src/modules/user/guard-auth/auth.local.guard.ts

@@ -0,0 +1,15 @@
+import { CanActivateFn } from '@angular/router';
+
+export const authLocalGuard: CanActivateFn = (route, state) => {
+  // 检查当前本地存储中,是否有用户验证信息
+  let userAuth = localStorage.getItem("USER_AUTH")
+  if(userAuth){
+    return true;
+  }else{
+    // 暂时存储登陆前用户所在页面
+    let REDIRECT_URL = location.pathname;
+    localStorage.setItem("REDIRECT_URL",REDIRECT_URL);
+    location.href = "/user/login"
+    return false;
+  }
+};

+ 35 - 0
src/modules/user/page-info/page-info.component.html

@@ -0,0 +1,35 @@
+
+<ion-header>
+    <ion-toolbar>
+      <ion-title>编辑资料</ion-title>
+    </ion-toolbar>
+  </ion-header>
+  <ion-content color="light">
+    <ion-card>
+        <img alt="User Background" src="https://imgsource.huashi6.com/images/ai/2023/9/27/20_326650.jpg?e=1695819140&token=qFZErZx7WS1v5B4rgQE2KLMHlYHVNaCuXeaA9OLD:XUO7gAGwc99tTXYGsF0R5itDsTg=" />
+      
+        <ion-card-content>
+            <ion-input [(ngModel)]="userInfo.nickname" label="用户昵称" labelPlacement="floating" [counter]="true" maxlength="20">
+            </ion-input>
+            <ion-input [(ngModel)]="userInfo.desc" label="个性签名" labelPlacement="floating" [counter]="true" maxlength="50">
+            </ion-input>
+        </ion-card-content>
+      </ion-card>
+
+      <ion-list [inset]="true">
+        <ion-item>
+            <ion-select [(ngModel)]="userInfo.gender" label="性别" placeholder="您的性别">
+              <ion-select-option value="男">男</ion-select-option>
+              <ion-select-option value="女">女</ion-select-option>
+              <ion-select-option value="未知">未知</ion-select-option>
+            </ion-select>
+          </ion-item>
+      </ion-list>
+
+    <ion-list [inset]="true">
+        <ion-button expand="block" (click)="saveInfo()">保存</ion-button>
+        <ion-button expand="block" fill="clear" routerLink="/project/mine">返回</ion-button>
+    </ion-list>
+  </ion-content>
+
+

+ 0 - 0
src/modules/user/page-info/page-info.component.scss


+ 21 - 0
src/modules/user/page-info/page-info.component.spec.ts

@@ -0,0 +1,21 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PageInfoComponent } from './page-info.component';
+
+describe('PageInfoComponent', () => {
+  let component: PageInfoComponent;
+  let fixture: ComponentFixture<PageInfoComponent>;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      declarations: [PageInfoComponent]
+    });
+    fixture = TestBed.createComponent(PageInfoComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 31 - 0
src/modules/user/page-info/page-info.component.ts

@@ -0,0 +1,31 @@
+import { Component } from '@angular/core';
+import { Router } from '@angular/router';
+import { UserService } from '../service-user/user.service';
+import * as Parse from "parse";
+
+@Component({
+  selector: 'app-page-info',
+  templateUrl: './page-info.component.html',
+  styleUrls: ['./page-info.component.scss']
+})
+export class PageInfoComponent {
+  constructor(
+    public userServ:UserService,
+    private router:Router
+  ){
+    this.userInfo = Parse.User.current()?.toJSON();
+  }
+  userInfo:any = {}
+  async saveInfo(){
+    let user = Parse.User.current();
+    console.log(this.userInfo)
+    delete this.userInfo.objectId
+    delete this.userInfo.username
+    delete this.userInfo.sessionToken
+    delete this.userInfo.updatedAt
+ 
+    user?.set(this.userInfo)
+    await user?.save()
+    this.router.navigate(["/project/mine"])
+  }
+}

+ 52 - 0
src/modules/user/page-login/page-login.component.html

@@ -0,0 +1,52 @@
+<body class="主体区" style="background-image: url(assets/register.png);background-size: cover;
+background-position: center;">
+  <!-- IonicModule + FormsModule实现,默认好看 -->
+<div class="login-modal">
+    <ion-card>
+        <ion-card-header>
+        <ion-card-title>登录</ion-card-title>
+        <ion-card-subtitle>该页面需登陆可继续使用</ion-card-subtitle>
+        </ion-card-header>
+    
+        <ion-card-content>
+        <ion-input [(ngModel)]="userData.userId"
+            label="QQ号码" 
+            labelPlacement="floating" 
+            [counter]="true" 
+            maxlength="20">
+        </ion-input>
+
+        <ion-input [(ngModel)]="userData.password"
+            label="密码"
+            labelPlacement="floating"
+            [counter]="true"
+            maxlength="20"
+        ></ion-input>
+            
+        <p style="color: red;">{{wrong}}</p>
+
+        </ion-card-content>
+    
+        <ion-button expand="block" (click)="login()">登录</ion-button>
+        <ion-button expand="block" routerLink="/user/register">注册</ion-button>
+        <ion-button expand="block" fill="clear" (click)="goBack()">返回</ion-button>
+    </ion-card>
+</div>
+
+
+  <!-- 纯HTML + FormsModule实现,可任意定制样式 -->
+<ng-container *ngIf="false">
+
+    请输入用户名:{{userData.userId}} <br/>
+    
+    <input [(ngModel)]="userData.userId" type="text"/><br/>
+    
+    请输入密码:{{userData.password}}<br/>
+    <input [(ngModel)]="userData.password" type="password"/><br/>
+    
+    <button (click)="login()">登录</button>
+    <ion-button expand="block" routerLink="/user/register">注册</ion-button>
+    <p style="color: red;">密码错误请确认后点击登入</p>
+
+</ng-container>
+</body>

+ 10 - 0
src/modules/user/page-login/page-login.component.scss

@@ -0,0 +1,10 @@
+.login-modal{
+    height:100vh;
+    width: 100vw;
+    display: flex;
+    flex-direction: column;
+    vertical-align: middle;
+    justify-content: center;
+}
+
+

+ 21 - 0
src/modules/user/page-login/page-login.component.spec.ts

@@ -0,0 +1,21 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PageLoginComponent } from './page-login.component';
+
+describe('PageLoginComponent', () => {
+  let component: PageLoginComponent;
+  let fixture: ComponentFixture<PageLoginComponent>;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      declarations: [PageLoginComponent]
+    });
+    fixture = TestBed.createComponent(PageLoginComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 51 - 0
src/modules/user/page-login/page-login.component.ts

@@ -0,0 +1,51 @@
+import { Component } from '@angular/core';
+import { Router } from '@angular/router';
+import { AlertController } from '@ionic/angular';
+import { UserService } from '../service-user/user.service';
+
+@Component({
+  selector: 'app-page-login',
+  templateUrl: './page-login.component.html',
+  styleUrls: ['./page-login.component.scss']
+})
+export class PageLoginComponent {
+  wrong:any
+  constructor(
+    private userServ:UserService,
+    private router:Router,
+    private alertCtrl:AlertController
+    ){}
+  userData:any = {
+    username:"",
+    password:"",
+    userId:""
+  }
+
+  goBack(){
+    let path = localStorage.getItem("REDIRECT_URL") || "../"
+    this.router.navigate([path])
+  }
+  async login(){
+    try{
+      let isLogin = await this.userServ.loginUserPassword(this.userData)
+      if(isLogin){
+        this.goBack()
+      }
+      else{
+        this.wrong="密码错误请确认后点击登入"
+        this.alertError()
+      }
+    }catch(err){
+      console.log(err)
+    }
+  }
+  async alertError(){
+    const alert = await this.alertCtrl.create({
+      header: '登陆失败',
+      subHeader: '请检查用户名密码',
+      buttons: ['好的'],
+    });
+
+    await alert.present();
+  }
+}

+ 36 - 0
src/modules/user/page-register/page-register.component.html

@@ -0,0 +1,36 @@
+<body class="主体区" style="background-image: url(assets/register.png);background-size: cover;
+background-position: center;">
+    <div class="注册框">
+        <form action="">
+            <h2>注册</h2>
+            <div>
+                <span>昵称</span>
+                <input type="text" class="text" [(ngModel)]="userData.username" placeholder="请输入昵称" [ngModelOptions]="{standalone: true}">
+            </div>
+            <div>
+                <span>QQ号码</span>
+                <input type="number" class="text" [(ngModel)]="userData.userId" placeholder="(用于用户头像)" [ngModelOptions]="{standalone: true}">
+            </div>
+            <div>
+                <span class="sex">性别</span>
+                <input type="radio" name="sex" [(ngModel)]="userData.sex" [ngModelOptions]="{standalone: true}" value="男">男
+                <input type="radio" name="sex" [(ngModel)]="userData.sex" [ngModelOptions]="{standalone: true}" value="女">女
+            </div>            
+            <div>
+                <span>密码</span>
+                <input type="password" class="text" [(ngModel)]="userData.password" placeholder="请输入密码" [ngModelOptions]="{standalone: true}">
+            </div>
+            <div>
+                <span>再次确认密码</span>
+                <input type="password" class="text" [(ngModel)]="userData.repassword" placeholder="请再次确认密码" [ngModelOptions]="{standalone: true}">
+            </div>
+            <div>
+                <input type="button" (click)="registerUser()" class="提交" value="注册">
+            </div>
+            {{wrong}}
+            <div>
+                <input routerLink="/user/login" type="button" class="提交" value="返回登录">
+            </div>
+        </form>
+    </div>
+</body>

+ 60 - 0
src/modules/user/page-register/page-register.component.scss

@@ -0,0 +1,60 @@
+.主体区 {
+    padding: 30px;
+    display: flex;
+    align-items: center;
+    flex-direction: column;
+}
+ 
+.注册框 {
+    width: 360px;
+    height: 500px;
+    padding-top: 50px;
+    padding-bottom: 50px;
+ 
+    border-radius: 10px;
+    background-color: rgba(255,255,255,0.7);
+}
+ 
+form {
+    height: 100%;
+ 
+    display: flex;
+    align-items: center;
+    flex-direction: column;
+    justify-content: space-between;
+}
+ 
+h2 {
+    font-size: 20px;
+}
+ 
+span {
+    display: inline-block;
+    width: 120px;
+    text-align: left;
+}
+.sex {
+    width: 225px;
+}
+ 
+.hobby {
+    width: 125px;
+}
+ 
+.提交 {
+    width: 290px;
+    height: 30px;
+ 
+    background: rgba(77,132,226);
+    color: white;
+    border: none;
+}
+ 
+.text {
+    width: 165px;
+    height: 25px;
+    padding-left: 10px;
+ 
+    border-radius: 5px;
+    border: none;
+}

+ 21 - 0
src/modules/user/page-register/page-register.component.spec.ts

@@ -0,0 +1,21 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PageRegisterComponent } from './page-register.component';
+
+describe('PageRegisterComponent', () => {
+  let component: PageRegisterComponent;
+  let fixture: ComponentFixture<PageRegisterComponent>;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      declarations: [PageRegisterComponent]
+    });
+    fixture = TestBed.createComponent(PageRegisterComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 113 - 0
src/modules/user/page-register/page-register.component.ts

@@ -0,0 +1,113 @@
+import { Component } from '@angular/core';
+import * as Parse from "parse";
+
+
+async function registerUser(userData: {
+  username: string;
+  password: string;
+  userId:number;
+  repassword:string
+  sex:string
+},wrong:any){
+  console.log("ok")
+      const query = new Parse.Query('GzpUser');
+      query.equalTo('username', userData.username);
+      const results = await query.find();
+      if ((results).length > 0) {
+        
+        wrong='该账号已经注册了'
+        console.log(wrong)
+      }else{
+        if(userData.userId<=10000){
+          wrong="QQ号码小于五位数,不符合要求。"
+        }
+        else{
+          if(userData.password==userData.repassword){
+            console.log("ok")
+            const Gzpuser = Parse.Object.extend('GzpUser');
+            const user = new Gzpuser();
+            user.set(userData.username,'usernamer')
+            user.set(userData.userId,'userId')
+            user.set(userData.password,'password')
+            user.set(userData.sex,'sex')
+            user.save().then(
+              (result: any) => {
+                console.log('对话消息已保存到数据库:', result);
+              },
+              (error: any) => {
+                console.error('保存对话消息时出错:', error);
+              }
+            );
+            wrong = '注册成功'
+          }else{
+            wrong="两次密码输入不一致"
+          }
+        }
+      }
+      console.log(wrong)
+    return wrong
+  }
+  
+
+@Component({
+  selector: 'app-page-register',
+  templateUrl: './page-register.component.html',
+  styleUrls: ['./page-register.component.scss']
+})
+
+export class PageRegisterComponent {
+  wrong:any=''
+  userData:any = {
+    userId:"",
+    username:"",
+    password:"",
+    repassword:"",
+    sex:'',
+  }
+  constructor() {
+    (Parse as any).serverURL = "https://web2023.fmode.cn/parse";
+    Parse.initialize("dev");
+    registerUser(this.userData,this.wrong)
+  }
+  // sent(){
+  //   console.log("ok")
+  //   registerUser(this.userData,this.wrong)
+  //   console.log(this.wrong)
+  // }
+  async registerUser(){
+    console.log(this.userData)
+        const query = new Parse.Query('GzpUser');
+        query.equalTo('userId', this.userData.userId);
+        const results = await query.find();
+        if ((results).length > 0) {
+          this.wrong='该QQ号码已经注册过了'
+          console.log(this.wrong)
+        }else{
+          if(this.userData.userId<=10000){
+            this.wrong="QQ号码小于五位数,不符合要求。"
+          }
+          else{
+            if(this.userData.password==this.userData.repassword){
+              const Gzpuser = Parse.Object.extend('GzpUser');
+              const user = new Gzpuser();
+              user.set('username',this.userData.username)
+              user.set('userId',this.userData.userId)
+              user.set('password',this.userData.password)
+              user.set('sex',this.userData.sex)
+              user.save().then(
+                (result: any) => {
+                  console.log('对话消息已保存到数据库:', result);
+                },
+                (error: any) => {
+                  console.error('保存对话消息时出错:', error);
+                }
+              );
+              this.wrong='注册成功'
+            }else{
+              this.wrong="两次密码输入不一致"
+            }
+          }
+        }
+        console.log(this.wrong)
+    }
+}

+ 25 - 0
src/modules/user/service-user/Chat.service.ts

@@ -0,0 +1,25 @@
+import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class ChatService {
+  private apiUrl = 'https://web2023.fmode.cn/parse'; // Parse后端API地址
+
+  constructor(private http: HttpClient) {}
+
+  // 获取聊天记录方法
+  async getChatHistory(userId: string): Promise<any> {
+    const url = `${this.apiUrl}/classes/Gzpmessage`; // Message为存储聊天记录的Parse类名
+    const query = { senderId: userId }; // 假设使用senderId字段来表示发送者ID
+    const headers = { 'X-Parse-Application-Id': 'dev' }; // Parse应用ID
+
+    try {
+      const response = await this.http.get(url, { headers, params: query }).toPromise();
+      console.log(response)
+    } catch (error) {
+      throw new Error('Failed to get chat history.');
+    }
+  }
+}

+ 41 - 0
src/modules/user/service-user/Share.service.ts

@@ -0,0 +1,41 @@
+import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class SharedService {
+  private userId: string = '';
+  private username: string = '';
+  private userImgScr: string = '';
+  private islogin: boolean = false;
+
+  setUsername(username: string ,userId:string): void {
+    this.username = username;
+    this.userId=userId
+    if(username!=null){
+      this.userImgScr="http://q1.qlogo.cn/g?b=qq&nk="+userId+"&s=100"
+    }
+  }
+  
+  getUsername(): string {
+    return this.username;
+  }
+  getUserId(): string {
+    return this.userId;
+  }
+  getUserImg(): string {
+    return this.userImgScr;
+  }
+  pdlogin(): boolean {
+    return this.islogin;
+  }
+  isLogin():void{
+    this.islogin=true;
+  }
+  outLogin():void{
+    this.userImgScr='';
+    this.username='';
+    this.islogin=false
+  }
+}

+ 16 - 0
src/modules/user/service-user/user.service.spec.ts

@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { UserService } from './user.service';
+
+describe('UserService', () => {
+  let service: UserService;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({});
+    service = TestBed.inject(UserService);
+  });
+
+  it('should be created', () => {
+    expect(service).toBeTruthy();
+  });
+});

+ 48 - 0
src/modules/user/service-user/user.service.ts

@@ -0,0 +1,48 @@
+import { Injectable } from '@angular/core';
+import { SharedService } from './Share.service';
+import * as Parse from "parse";
+
+@Injectable({
+  providedIn: 'root'
+})
+export class UserService {
+
+
+  
+  constructor(private sharedService: SharedService) {
+    (Parse as any).serverURL = "https://web2023.fmode.cn/parse";
+    Parse.initialize("dev");
+  }
+
+  /**
+   * 检查用户密码是否正确
+   */
+  async loginUserPassword(userData: {
+    username: any;
+    password: string;
+    userId:string;
+  }) {
+    try {
+      const query = new Parse.Query('GzpUser');
+      query.equalTo('userId', userData.userId);
+      query.equalTo('password', userData.password);
+
+      const results = await query.find();
+      userData.username = results.map(user => user.get('username'));
+      if (results.length > 0) {
+        this.sharedService.isLogin()
+        this.sharedService.setUsername(userData.username,userData.userId);
+        return true; // 匹配成功
+        
+      }else{
+
+        return false;
+      }
+    } catch (err) {
+      console.error(err);
+      return false;
+    }
+  }
+}
+export { SharedService };
+

+ 20 - 0
src/modules/user2/guard-auth/auth.guard.ts

@@ -0,0 +1,20 @@
+import { CanActivateFn } from '@angular/router';
+import * as Parse from "parse";
+
+export const authGuard: CanActivateFn = (route, state) => {
+  // 检查当前本地存储中,是否有用户验证信息
+  let userAuth = Parse.User.current();
+  if(userAuth?.id){
+    return true;
+  }else{
+    // 暂时存储登陆前用户所在页面
+    let REDIRECT_URL = location.pathname;
+    localStorage.setItem("REDIRECT_URL",REDIRECT_URL);
+    if (location.pathname?.startsWith("/s1234567")){
+        location.href = "/s1234567/user/login"
+    }else{
+        location.href = "/user/login"
+    }
+    return false;
+  }
+};

+ 15 - 0
src/modules/user2/guard-auth/auth.local.guard.ts

@@ -0,0 +1,15 @@
+import { CanActivateFn } from '@angular/router';
+
+export const authLocalGuard: CanActivateFn = (route, state) => {
+  // 检查当前本地存储中,是否有用户验证信息
+  let userAuth = localStorage.getItem("USER_AUTH")
+  if(userAuth){
+    return true;
+  }else{
+    // 暂时存储登陆前用户所在页面
+    let REDIRECT_URL = location.pathname;
+    localStorage.setItem("REDIRECT_URL",REDIRECT_URL);
+    location.href = "/user/login"
+    return false;
+  }
+};

+ 35 - 0
src/modules/user2/page-info/page-info.component.html

@@ -0,0 +1,35 @@
+
+<ion-header>
+    <ion-toolbar>
+      <ion-title>编辑资料</ion-title>
+    </ion-toolbar>
+  </ion-header>
+  <ion-content color="light">
+    <ion-card>
+        <img alt="User Background" src="https://imgsource.huashi6.com/images/ai/2023/9/27/20_326650.jpg?e=1695819140&token=qFZErZx7WS1v5B4rgQE2KLMHlYHVNaCuXeaA9OLD:XUO7gAGwc99tTXYGsF0R5itDsTg=" />
+      
+        <ion-card-content>
+            <ion-input [(ngModel)]="userInfo.nickname" label="用户昵称" labelPlacement="floating" [counter]="true" maxlength="20">
+            </ion-input>
+            <ion-input [(ngModel)]="userInfo.desc" label="个性签名" labelPlacement="floating" [counter]="true" maxlength="50">
+            </ion-input>
+        </ion-card-content>
+      </ion-card>
+
+      <ion-list [inset]="true">
+        <ion-item>
+            <ion-select [(ngModel)]="userInfo.gender" label="性别" placeholder="您的性别">
+              <ion-select-option value="男">男</ion-select-option>
+              <ion-select-option value="女">女</ion-select-option>
+              <ion-select-option value="未知">未知</ion-select-option>
+            </ion-select>
+          </ion-item>
+      </ion-list>
+
+    <ion-list [inset]="true">
+        <ion-button expand="block" (click)="saveInfo()">保存</ion-button>
+        <ion-button expand="block" fill="clear" routerLink="/project/mine">返回</ion-button>
+    </ion-list>
+  </ion-content>
+
+

+ 0 - 0
src/modules/user2/page-info/page-info.component.scss


+ 21 - 0
src/modules/user2/page-info/page-info.component.spec.ts

@@ -0,0 +1,21 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PageInfoComponent } from './page-info.component';
+
+describe('PageInfoComponent', () => {
+  let component: PageInfoComponent;
+  let fixture: ComponentFixture<PageInfoComponent>;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      declarations: [PageInfoComponent]
+    });
+    fixture = TestBed.createComponent(PageInfoComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 31 - 0
src/modules/user2/page-info/page-info.component.ts

@@ -0,0 +1,31 @@
+import { Component } from '@angular/core';
+import { Router } from '@angular/router';
+import { UserService } from '../service-user/user.service';
+import * as Parse from "parse";
+
+@Component({
+  selector: 'app-page-info',
+  templateUrl: './page-info.component.html',
+  styleUrls: ['./page-info.component.scss']
+})
+export class PageInfoComponent {
+  constructor(
+    public userServ:UserService,
+    private router:Router
+  ){
+    this.userInfo = Parse.User.current()?.toJSON();
+  }
+  userInfo:any = {}
+  async saveInfo(){
+    let user = Parse.User.current();
+    console.log(this.userInfo)
+    delete this.userInfo.objectId
+    delete this.userInfo.username
+    delete this.userInfo.sessionToken
+    delete this.userInfo.updatedAt
+ 
+    user?.set(this.userInfo)
+    await user?.save()
+    this.router.navigate(["/project/mine"])
+  }
+}

+ 52 - 0
src/modules/user2/page-login/page-login.component.html

@@ -0,0 +1,52 @@
+<body class="主体区" style="background-image: url(assets/register.png);background-size: cover;
+background-position: center;">
+  <!-- IonicModule + FormsModule实现,默认好看 -->
+<div class="login-modal">
+    <ion-card>
+        <ion-card-header>
+        <ion-card-title>登录</ion-card-title>
+        <ion-card-subtitle>该页面需登陆可继续使用</ion-card-subtitle>
+        </ion-card-header>
+    
+        <ion-card-content>
+        <ion-input [(ngModel)]="userData.userId"
+            label="QQ号码" 
+            labelPlacement="floating" 
+            [counter]="true" 
+            maxlength="20">
+        </ion-input>
+
+        <ion-input [(ngModel)]="userData.password"
+            label="密码"
+            labelPlacement="floating"
+            [counter]="true"
+            maxlength="20"
+        ></ion-input>
+            
+        <p style="color: red;">{{wrong}}</p>
+
+        </ion-card-content>
+    
+        <ion-button expand="block" (click)="login()">登录</ion-button>
+        <ion-button expand="block" routerLink="/user/register">注册</ion-button>
+        <ion-button expand="block" fill="clear" (click)="goBack()">返回</ion-button>
+    </ion-card>
+</div>
+
+
+  <!-- 纯HTML + FormsModule实现,可任意定制样式 -->
+<ng-container *ngIf="false">
+
+    请输入用户名:{{userData.userId}} <br/>
+    
+    <input [(ngModel)]="userData.userId" type="text"/><br/>
+    
+    请输入密码:{{userData.password}}<br/>
+    <input [(ngModel)]="userData.password" type="password"/><br/>
+    
+    <button (click)="login()">登录</button>
+    <ion-button expand="block" routerLink="/user/register">注册</ion-button>
+    <p style="color: red;">密码错误请确认后点击登入</p>
+
+</ng-container>
+</body>

+ 10 - 0
src/modules/user2/page-login/page-login.component.scss

@@ -0,0 +1,10 @@
+.login-modal{
+    height:100vh;
+    width: 100vw;
+    display: flex;
+    flex-direction: column;
+    vertical-align: middle;
+    justify-content: center;
+}
+
+

+ 21 - 0
src/modules/user2/page-login/page-login.component.spec.ts

@@ -0,0 +1,21 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PageLoginComponent } from './page-login.component';
+
+describe('PageLoginComponent', () => {
+  let component: PageLoginComponent;
+  let fixture: ComponentFixture<PageLoginComponent>;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      declarations: [PageLoginComponent]
+    });
+    fixture = TestBed.createComponent(PageLoginComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 51 - 0
src/modules/user2/page-login/page-login.component.ts

@@ -0,0 +1,51 @@
+import { Component } from '@angular/core';
+import { Router } from '@angular/router';
+import { AlertController } from '@ionic/angular';
+import { UserService } from '../service-user/user.service';
+
+@Component({
+  selector: 'app-page-login',
+  templateUrl: './page-login.component.html',
+  styleUrls: ['./page-login.component.scss']
+})
+export class PageLoginComponent {
+  wrong:any
+  constructor(
+    private userServ:UserService,
+    private router:Router,
+    private alertCtrl:AlertController
+    ){}
+  userData:any = {
+    username:"",
+    password:"",
+    userId:""
+  }
+
+  goBack(){
+    let path = localStorage.getItem("REDIRECT_URL") || "../"
+    this.router.navigate([path])
+  }
+  async login(){
+    try{
+      let isLogin = await this.userServ.loginUserPassword(this.userData)
+      if(isLogin){
+        this.goBack()
+      }
+      else{
+        this.wrong="密码错误请确认后点击登入"
+        this.alertError()
+      }
+    }catch(err){
+      console.log(err)
+    }
+  }
+  async alertError(){
+    const alert = await this.alertCtrl.create({
+      header: '登陆失败',
+      subHeader: '请检查用户名密码',
+      buttons: ['好的'],
+    });
+
+    await alert.present();
+  }
+}

+ 36 - 0
src/modules/user2/page-register/page-register.component.html

@@ -0,0 +1,36 @@
+<body class="主体区" style="background-image: url(assets/register.png);background-size: cover;
+background-position: center;">
+    <div class="注册框">
+        <form action="">
+            <h2>注册</h2>
+            <div>
+                <span>昵称</span>
+                <input type="text" class="text" [(ngModel)]="userData.username" placeholder="请输入昵称" [ngModelOptions]="{standalone: true}">
+            </div>
+            <div>
+                <span>QQ号码</span>
+                <input type="number" class="text" [(ngModel)]="userData.userId" placeholder="(用于用户头像)" [ngModelOptions]="{standalone: true}">
+            </div>
+            <div>
+                <span class="sex">性别</span>
+                <input type="radio" name="sex" [(ngModel)]="userData.sex" [ngModelOptions]="{standalone: true}" value="男">男
+                <input type="radio" name="sex" [(ngModel)]="userData.sex" [ngModelOptions]="{standalone: true}" value="女">女
+            </div>            
+            <div>
+                <span>密码</span>
+                <input type="password" class="text" [(ngModel)]="userData.password" placeholder="请输入密码" [ngModelOptions]="{standalone: true}">
+            </div>
+            <div>
+                <span>再次确认密码</span>
+                <input type="password" class="text" [(ngModel)]="userData.repassword" placeholder="请再次确认密码" [ngModelOptions]="{standalone: true}">
+            </div>
+            <div>
+                <input type="button" (click)="registerUser()" class="提交" value="注册">
+            </div>
+            {{wrong}}
+            <div>
+                <input routerLink="/user/login" type="button" class="提交" value="返回登录">
+            </div>
+        </form>
+    </div>
+</body>

+ 60 - 0
src/modules/user2/page-register/page-register.component.scss

@@ -0,0 +1,60 @@
+.主体区 {
+    padding: 30px;
+    display: flex;
+    align-items: center;
+    flex-direction: column;
+}
+ 
+.注册框 {
+    width: 360px;
+    height: 500px;
+    padding-top: 50px;
+    padding-bottom: 50px;
+ 
+    border-radius: 10px;
+    background-color: rgba(255,255,255,0.7);
+}
+ 
+form {
+    height: 100%;
+ 
+    display: flex;
+    align-items: center;
+    flex-direction: column;
+    justify-content: space-between;
+}
+ 
+h2 {
+    font-size: 20px;
+}
+ 
+span {
+    display: inline-block;
+    width: 120px;
+    text-align: left;
+}
+.sex {
+    width: 225px;
+}
+ 
+.hobby {
+    width: 125px;
+}
+ 
+.提交 {
+    width: 290px;
+    height: 30px;
+ 
+    background: rgba(77,132,226);
+    color: white;
+    border: none;
+}
+ 
+.text {
+    width: 165px;
+    height: 25px;
+    padding-left: 10px;
+ 
+    border-radius: 5px;
+    border: none;
+}

+ 21 - 0
src/modules/user2/page-register/page-register.component.spec.ts

@@ -0,0 +1,21 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PageRegisterComponent } from './page-register.component';
+
+describe('PageRegisterComponent', () => {
+  let component: PageRegisterComponent;
+  let fixture: ComponentFixture<PageRegisterComponent>;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      declarations: [PageRegisterComponent]
+    });
+    fixture = TestBed.createComponent(PageRegisterComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 113 - 0
src/modules/user2/page-register/page-register.component.ts

@@ -0,0 +1,113 @@
+import { Component } from '@angular/core';
+import * as Parse from "parse";
+
+
+async function registerUser(userData: {
+  username: string;
+  password: string;
+  userId:number;
+  repassword:string
+  sex:string
+},wrong:any){
+  console.log("ok")
+      const query = new Parse.Query('GzpUser');
+      query.equalTo('username', userData.username);
+      const results = await query.find();
+      if ((results).length > 0) {
+        
+        wrong='该账号已经注册了'
+        console.log(wrong)
+      }else{
+        if(userData.userId<=10000){
+          wrong="QQ号码小于五位数,不符合要求。"
+        }
+        else{
+          if(userData.password==userData.repassword){
+            console.log("ok")
+            const Gzpuser = Parse.Object.extend('GzpUser');
+            const user = new Gzpuser();
+            user.set(userData.username,'usernamer')
+            user.set(userData.userId,'userId')
+            user.set(userData.password,'password')
+            user.set(userData.sex,'sex')
+            user.save().then(
+              (result: any) => {
+                console.log('对话消息已保存到数据库:', result);
+              },
+              (error: any) => {
+                console.error('保存对话消息时出错:', error);
+              }
+            );
+            wrong = '注册成功'
+          }else{
+            wrong="两次密码输入不一致"
+          }
+        }
+      }
+      console.log(wrong)
+    return wrong
+  }
+  
+
+@Component({
+  selector: 'app-page-register',
+  templateUrl: './page-register.component.html',
+  styleUrls: ['./page-register.component.scss']
+})
+
+export class PageRegisterComponent {
+  wrong:any=''
+  userData:any = {
+    userId:"",
+    username:"",
+    password:"",
+    repassword:"",
+    sex:'',
+  }
+  constructor() {
+    (Parse as any).serverURL = "https://web2023.fmode.cn/parse";
+    Parse.initialize("dev");
+    registerUser(this.userData,this.wrong)
+  }
+  // sent(){
+  //   console.log("ok")
+  //   registerUser(this.userData,this.wrong)
+  //   console.log(this.wrong)
+  // }
+  async registerUser(){
+    console.log(this.userData)
+        const query = new Parse.Query('GzpUser');
+        query.equalTo('userId', this.userData.userId);
+        const results = await query.find();
+        if ((results).length > 0) {
+          this.wrong='该QQ号码已经注册过了'
+          console.log(this.wrong)
+        }else{
+          if(this.userData.userId<=10000){
+            this.wrong="QQ号码小于五位数,不符合要求。"
+          }
+          else{
+            if(this.userData.password==this.userData.repassword){
+              const Gzpuser = Parse.Object.extend('GzpUser');
+              const user = new Gzpuser();
+              user.set('username',this.userData.username)
+              user.set('userId',this.userData.userId)
+              user.set('password',this.userData.password)
+              user.set('sex',this.userData.sex)
+              user.save().then(
+                (result: any) => {
+                  console.log('对话消息已保存到数据库:', result);
+                },
+                (error: any) => {
+                  console.error('保存对话消息时出错:', error);
+                }
+              );
+              this.wrong='注册成功'
+            }else{
+              this.wrong="两次密码输入不一致"
+            }
+          }
+        }
+        console.log(this.wrong)
+    }
+}

+ 25 - 0
src/modules/user2/service-user/Chat.service.ts

@@ -0,0 +1,25 @@
+import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class ChatService {
+  private apiUrl = 'https://web2023.fmode.cn/parse'; // Parse后端API地址
+
+  constructor(private http: HttpClient) {}
+
+  // 获取聊天记录方法
+  async getChatHistory(userId: string): Promise<any> {
+    const url = `${this.apiUrl}/classes/Gzpmessage`; // Message为存储聊天记录的Parse类名
+    const query = { senderId: userId }; // 假设使用senderId字段来表示发送者ID
+    const headers = { 'X-Parse-Application-Id': 'dev' }; // Parse应用ID
+
+    try {
+      const response = await this.http.get(url, { headers, params: query }).toPromise();
+      console.log(response)
+    } catch (error) {
+      throw new Error('Failed to get chat history.');
+    }
+  }
+}

+ 41 - 0
src/modules/user2/service-user/Share.service.ts

@@ -0,0 +1,41 @@
+import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class SharedService {
+  private userId: string = '';
+  private username: string = '';
+  private userImgScr: string = '';
+  private islogin: boolean = false;
+
+  setUsername(username: string ,userId:string): void {
+    this.username = username;
+    this.userId=userId
+    if(username!=null){
+      this.userImgScr="http://q1.qlogo.cn/g?b=qq&nk="+userId+"&s=100"
+    }
+  }
+  
+  getUsername(): string {
+    return this.username;
+  }
+  getUserId(): string {
+    return this.userId;
+  }
+  getUserImg(): string {
+    return this.userImgScr;
+  }
+  pdlogin(): boolean {
+    return this.islogin;
+  }
+  isLogin():void{
+    this.islogin=true;
+  }
+  outLogin():void{
+    this.userImgScr='';
+    this.username='';
+    this.islogin=false
+  }
+}

+ 16 - 0
src/modules/user2/service-user/user.service.spec.ts

@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { UserService } from './user.service';
+
+describe('UserService', () => {
+  let service: UserService;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({});
+    service = TestBed.inject(UserService);
+  });
+
+  it('should be created', () => {
+    expect(service).toBeTruthy();
+  });
+});

+ 48 - 0
src/modules/user2/service-user/user.service.ts

@@ -0,0 +1,48 @@
+import { Injectable } from '@angular/core';
+import { SharedService } from './Share.service';
+import * as Parse from "parse";
+
+@Injectable({
+  providedIn: 'root'
+})
+export class UserService {
+
+
+  
+  constructor(private sharedService: SharedService) {
+    (Parse as any).serverURL = "https://web2023.fmode.cn/parse";
+    Parse.initialize("dev");
+  }
+
+  /**
+   * 检查用户密码是否正确
+   */
+  async loginUserPassword(userData: {
+    username: any;
+    password: string;
+    userId:string;
+  }) {
+    try {
+      const query = new Parse.Query('GzpUser');
+      query.equalTo('userId', userData.userId);
+      query.equalTo('password', userData.password);
+
+      const results = await query.find();
+      userData.username = results.map(user => user.get('username'));
+      if (results.length > 0) {
+        this.sharedService.isLogin()
+        this.sharedService.setUsername(userData.username,userData.userId);
+        return true; // 匹配成功
+        
+      }else{
+
+        return false;
+      }
+    } catch (err) {
+      console.error(err);
+      return false;
+    }
+  }
+}
+export { SharedService };
+

+ 21 - 0
src/modules/user2/user-routing.module.ts

@@ -0,0 +1,21 @@
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+import { authGuard } from './guard-auth/auth.guard';
+import { PageInfoComponent } from './page-info/page-info.component';
+import { PageLoginComponent } from './page-login/page-login.component';
+import { PageRegisterComponent } from './page-register/page-register.component';
+
+const routes: Routes = [
+  {path:"login",component:PageLoginComponent},
+  {path:"register",component:PageRegisterComponent},
+  {
+    path:"info",component:PageInfoComponent,
+    canActivate:[authGuard]
+  },
+];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule]
+})
+export class UserRoutingModule { }

+ 25 - 0
src/modules/user2/user.module.ts

@@ -0,0 +1,25 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+
+import { UserRoutingModule } from './user-routing.module';
+import { PageLoginComponent } from './page-login/page-login.component';
+import { PageRegisterComponent } from './page-register/page-register.component';
+import { PageInfoComponent } from './page-info/page-info.component';
+import { FormsModule } from '@angular/forms';
+import { IonicModule } from '@ionic/angular';
+
+
+@NgModule({
+  declarations: [
+    PageLoginComponent,
+    PageRegisterComponent,
+    PageInfoComponent,
+  ],
+  imports: [
+    CommonModule,
+    UserRoutingModule,
+    FormsModule,
+    IonicModule,
+  ]
+})
+export class UserModule { }

Деякі файли не було показано, через те що забагато файлів було змінено