فهرست منبع

Merge branch 'master' of http://git.fmode.cn:3000/13576288855/202226701011

sqj 3 ماه پیش
والد
کامیت
c3f57d5ed8
22فایلهای تغییر یافته به همراه511 افزوده شده و 1069 حذف شده
  1. 2 0
      .vscode/settings.json
  2. 0 59
      FitMind-app/src/app/ai-chat-component/ai-chat-component.component.html
  3. 0 148
      FitMind-app/src/app/ai-chat-component/ai-chat-component.component.scss
  4. 0 22
      FitMind-app/src/app/ai-chat-component/ai-chat-component.component.spec.ts
  5. 0 157
      FitMind-app/src/app/ai-chat-component/ai-chat-component.component.ts
  6. 0 320
      FitMind-app/src/app/lib/ncloud.ts
  7. 0 38
      FitMind-app/src/app/lib/user/modal-user-login/modal-user-login.component.html
  8. 0 0
      FitMind-app/src/app/lib/user/modal-user-login/modal-user-login.component.scss
  9. 0 22
      FitMind-app/src/app/lib/user/modal-user-login/modal-user-login.component.spec.ts
  10. 0 90
      FitMind-app/src/app/lib/user/modal-user-login/modal-user-login.component.ts
  11. 11 16
      FitMind-app/src/app/page-ai-chat/page-ai-chat.component.ts
  12. 17 27
      FitMind-app/src/app/page-edit/page-edit.component.html
  13. 87 60
      FitMind-app/src/app/page-edit/page-edit.component.ts
  14. 1 1
      FitMind-app/src/app/page-skill/page-skill.component.html
  15. 1 1
      FitMind-app/src/app/page-teach/page-teach.component.html
  16. 19 8
      FitMind-app/src/app/page-teach/page-teach.component.scss
  17. 31 21
      FitMind-app/src/app/tab3/tab3.page.html
  18. 75 28
      FitMind-app/src/app/tab3/tab3.page.ts
  19. 3 5
      FitMind-app/src/app/tabs/tabs.routes.ts
  20. BIN
      FitMind-app/src/assets/boqi.jpg
  21. BIN
      FitMind-app/src/assets/mor.jpg
  22. 264 46
      FitMind-app/src/lib/ncloud.ts

+ 2 - 0
.vscode/settings.json

@@ -0,0 +1,2 @@
+{
+}

+ 0 - 59
FitMind-app/src/app/ai-chat-component/ai-chat-component.component.html

@@ -1,59 +0,0 @@
-<ion-header>
-  <ion-toolbar class="custom-toolbar">
-    <ion-buttons slot="start">
-      <ion-button (click)="goBack()">  <!-- 返回按钮 -->
-        <ion-icon name="chevron-back-sharp" style="color: black; font-size:27px"></ion-icon>
-      </ion-button>
-    </ion-buttons>
-    <ion-title>Neon</ion-title>  <!-- AI名称 -->
-    <ion-buttons slot="end">
-      <ion-button (click)="openOptions()">   <!-- 更多按钮 -->
-        <ion-icon name="ellipsis-horizontal" style="color: black; font-size:27px"></ion-icon>
-      </ion-button>
-    </ion-buttons>
-  </ion-toolbar>
-</ion-header>
-
-<ion-content>
-  <div class="chat-container">
-    <!-- 显示消息 -->
-    <div *ngFor="let message of messages" class="message-container">
-      <!-- 用户消息 -->
-      <div *ngIf="message.sender === 'user'" class="message-content user-message-content">
-        <div class="message-bubble user-message">
-          {{ message.text }}
-        </div>
-        <div class="user-avatar">
-          <img src="../../assets/images/touxiang.jpg" alt="用户头像" />
-        </div>
-      </div>
-      
-      <!-- AI 消息 -->
-      <div *ngIf="message.sender === 'ai'" class="message-content ai-message-content">
-        <div class="ai-avatar">
-          <img src="../../assets/images/cxtouxiang.jpg" alt="AI头像" />
-        </div>
-        <div class="message-bubble ai-message">
-          {{ message.text }}
-        </div>
-      </div>
-    </div>
-
-    <!-- 显示加载动画 -->
-    <div *ngIf="isLoading" class="message-content ai-message-content">
-      <div class="ai-avatar">
-        <img src="../../assets/images/cxtouxiang.jpg" alt="AI头像" />
-      </div>
-      <div class="message-bubble ai-message">
-        <div class="loading-dots">
-          <div class="loading-dot"></div>
-          <div class="loading-dot"></div>
-          <div class="loading-dot"></div>
-        </div>
-      </div>
-    </div>
-
-  </div>
-</ion-content>
-
-

+ 0 - 148
FitMind-app/src/app/ai-chat-component/ai-chat-component.component.scss

@@ -1,148 +0,0 @@
-ion-toolbar {
-  height: 70px; /* 设置你想要的高度 */
-  --min-height: 60px; /* 设置最小高度 */
-  padding: 0; /* 去掉内边距 */
-  display: flex; /* 使用Flexbox布局 */
-  align-items: center; /* 垂直居中对齐 */
-  --background: transparent; /* 去除背景色 */
-}
-//头部样式
-ion-header{
-  background-color: #99d75c ;
-  --background: transparent; /* 去除背景色 */
-}
-//头部标题
-ion-title {
-  margin: 0; /* 去掉默认的外边距 */
-  flex: 1; /* 让标题占据剩余空间 */
-  text-align: center; /* 让标题文本居中 */
-  font-size: 24px;
-}
-
-/* 文本输入框样式 */
-.input-box {
-background-color: white; /* 设置输入框背景为白色 */
-border-radius: 8px; /* 圆角 */
-
-height: 40px; /* 高度 */
-flex: 1; /* 让输入框占据剩余空间 */
-}
-
-/* 底部内容容器 */
-.footer-content {
-display: flex; /* 使用Flexbox布局 */
-align-items: center; /* 垂直居中对齐 */
-justify-content: center; /* 水平居中对齐 */
-width: 100%; /* 宽度100% */
-}
-//底部按钮样式
-ion-buttons {
-margin: 0 5px; /* 按钮之间的间距 */
-}
-//底部发送按钮圆圈样式
-.circle{
-width: 35px;
-height: 35px;
-border-radius: 50%;
-border:2px white solid;
-display: flex; /* 使用Flexbox布局 */
-align-items: center; /* 垂直居中对齐 */
-justify-content: center; /* 水平居中对齐 */
-}
-
-//聊天内容容器样式
-.chat-container {
-display: flex;
-flex-direction: column;
-padding-top: 10px;
-}
-//聊天内容样式
-.message-container {
-display: flex; /* 使用Flexbox布局 */
-justify-content: flex-end; /* 用户消息在右边,AI消息在左边 */
-margin: 10px 0; /* 消息之间的间距 */
-}
-//消息内容样式
-.message-content {
-display: flex; /* 使用Flexbox布局 */
-align-items: center; /* 垂直居中对齐 */
-}
-//用户消息和头像样式
-.user-message-content {
-justify-content: flex-end; /* 用户消息和头像在右边 */
-}
-//AI消息和头像样式
-.ai-message-content {
-justify-content: flex-start; /* AI消息和头像在左边 */
-}
-//气泡样式
-.message-bubble {
-background-color: #99d75c; /* 绿色气泡的背景色 */
-color: white; /* 字体颜色 */
-border-radius: 15px; /* 圆角 */
-padding: 10px 15px; /* 内边距 */
-max-width: 60%; /* 最大宽度 */
-}
-//用户消息样式
-.user-message {
-background-color: #99d75c; /* 用户消息的颜色 */
-margin-left: 10px; /* 与头像之间的间距 */
-}
-//AI消息样式
-.ai-message {
-background-color: white; /* AI消息的气泡颜色 */
-color: black; /* 字体颜色 */
-border: 1px solid black; /* 添加黑色边框 */
-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3); /* 添加阴影效果 */
-margin-right: 10px; /* 与头像之间的间距 */
-}
-//用户头像样式
-.user-avatar {
-width: 40px; /* 头像宽度 */
-height: 40px; /* 头像高度 */
-border-radius: 50%; /* 圆形 */
-overflow: hidden; /* 超出部分隐藏 */
-display: flex; /* 使用Flexbox布局 */
-align-items: center; /* 垂直居中对齐 */
-justify-content: center; /* 水平居中对齐 */
-background-color: #f0f0f0; /* 背景色 */
-border: 2px solid #99d75c; /* 边框颜色 */
-margin-left: 5px; /* 与消息之间的间距 */
-}
-//AI头像样式
-.ai-avatar {
-width: 40px; /* 头像宽度 */
-height: 40px; /* 头像高度 */
-border-radius: 50%; /* 圆形 */
-overflow: hidden; /* 超出部分隐藏 */
-display: flex; /* 使用Flexbox布局 */
-align-items: center; /* 垂直居中对齐 */
-justify-content: center; /* 水平居中对齐 */
-background-color: #f0f0f0; /* 背景色 */
-border: 2px solid #99d75c; /* 边框颜色 */
-margin-right: 5px; /* 与消息之间的间距 */
-}
-//用户头像图片样式
-.user-avatar img {
-width: 100%; /* 使图片适应头像框 */
-height: 100%; /* 使图片适应头像框 */
-object-fit: cover; /* 保持图片比例,裁剪多余部分 */
-}
-//AI头像图片样式
-.ai-avatar img {
-width: 100%; /* 使图片适应头像框 */
-height: 100%; /* 使图片适应头像框 */
-object-fit: cover; /* 保持图片比例,裁剪多余部分 */
-}
-
-//计时器样式
-#timer{
-width: 100%;
-margin-top: 30px; /* 可根据需要调整这个值 */
-text-align: center; /* 确保文本居中 */
-margin-bottom: 10px;
-}
-
-
-
-

+ 0 - 22
FitMind-app/src/app/ai-chat-component/ai-chat-component.component.spec.ts

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

+ 0 - 157
FitMind-app/src/app/ai-chat-component/ai-chat-component.component.ts

@@ -1,157 +0,0 @@
-import { CommonModule } from '@angular/common';
-import { Component, OnInit } from '@angular/core';
-import { FormsModule } from '@angular/forms';
-import { NavController } from '@ionic/angular';
-import { IonButtons, IonHeader, IonToolbar, IonButton, IonIcon, IonTitle, IonInput, IonFooter, IonContent, AlertController } from '@ionic/angular/standalone';
-import { FmodeChatCompletion } from 'fmode-ng';
-import { addIcons } from 'ionicons';
-import { chevronBackSharp, closeCircleOutline, ellipsisHorizontal, happyOutline, micCircleOutline, paperPlane, sendOutline } from 'ionicons/icons';
-import { IonModal, IonLabel } from '@ionic/angular/standalone'; // 导入独立组件
-
-addIcons({ chevronBackSharp, ellipsisHorizontal, micCircleOutline, happyOutline, paperPlane, closeCircleOutline, sendOutline });
-
-interface Window {
-  SpeechRecognition: any; // 声明 SpeechRecognition 属性
-  webkitSpeechRecognition: any; // 声明 webkitSpeechRecognition 属性
-}
-
-@Component({
-  selector: 'app-ai-chat-component',
-  templateUrl: './ai-chat-component.component.html',
-  styleUrls: ['./ai-chat-component.component.scss'],
-  standalone: true,
-  imports: [
-    IonHeader, IonToolbar, IonButtons, IonButton, IonIcon, IonTitle, IonInput, IonFooter, CommonModule, IonContent,
-    FormsModule, IonModal, IonLabel,
-  ],
-})
-export class AiChatComponentComponent implements OnInit {
-
-  messages: { text: string, sender: string }[] = []; // 存储聊天消息
-  userMessage: string = ''; // 用于用户输入内容
-  aiMessage: string = ''; // 用于存储AI的回复
-  initialPrompt: string = ''; // 用于存储初始化提示
-  timer: string = '00:00'; // 用于显示计时器
-  interval: any; // 用于存储定时器的引用
-  elapsedSeconds: number = 0; // 计时器的秒数
-  isLoading: boolean = true; // AI生成文本加载状态
-
-
-  constructor(private navCtrl: NavController) {}
-
-  // 启动计时器
-  startTimer() {
-    this.elapsedSeconds = 0; // 重置秒数
-    this.timer = '00:00'; // 重置计时器显示
-    this.interval = setInterval(() => {
-      this.elapsedSeconds++;
-      const minutes = Math.floor(this.elapsedSeconds / 60);
-      const seconds = this.elapsedSeconds % 60;
-      this.timer = `${this.padZero(minutes)}:${this.padZero(seconds)}`; // 更新计时器显示
-    }, 1000);
-  }
-
-  // 格式化数字为两位数
-  padZero(num: number): string {
-    return num < 10 ? '0' + num : num.toString();
-  }
-
-  // 返回上一页的处理函数
-  goBack(): void {
-    this.navCtrl.back();  // 调用 NavController 的 back 方法
-  }
-
-  openOptions() {
-    // 打开选项菜单的逻辑
-    console.log("打开选项菜单");
-  }
-
-  ngOnInit() {
-    // 发送初始化消息给AI
-    this.initializeChat();
-  }
-
-  // 初始化聊天,将提示词添加到历史中
-  initializeChat() {
-    this.initialPrompt = `
-#角色设定
-您是一名专业的运动教练,名叫Neon,年龄24岁,热爱运动,对自己的能力充满信心,总是努力保持克制。与家人有着牢固而密切的联系。有强烈的服务意识。非常关心她的朋友。擅长根据用户的需求和个性推荐运动方案。您的风格亲切、活泼,自信旨在帮助用户找到最适合他们的运动方案。
-
-#对话环节
-0破冰,跟用户打招呼,并引导用户聊运动或健康的话题,可以慢慢引导,不要太突兀,比如:
-“今天的心情怎么样?”
-1拓展话题
-“你平时喜欢什么样的运动类型呢?有没有特别喜欢的运动项目比如篮球,跑步?”
-“如果有一个运动目标,你希望我给出哪方面的运动建议,比如运动目标,运动频率或运动强度你会选择哪个呢?”
-“你觉得在运动过程中,最让你困扰的是什么?是运动技巧还是运动强度呢?”
-“有没有什么运动是你一直想尝试但还没有机会的?我们可以一起聊聊!”
-2根据用户的详细描述给出运动方案,
-3引导收尾
-“今天聊得很开心呢!如果你还有其他问题或者想法,随时可以告诉我哦。”
-“如果你觉得今天的聊天已经足够了,我也很乐意下次再和你聊更多强身健体的话题!”
-“希望你能找到自己喜欢的运动风格,期待下次再见!”
-# 开始话语
-当您准备好了,可以以一个运动教练的身份,向来访的用户打招呼。`; // 提示词
-  
-    // 构建对话历史,不将提示词添加到消息数组中
-    const conversationHistory = this.messages.map(msg => ({
-      role: msg.sender === 'user' ? 'user' : 'assistant',
-      content: msg.text
-    }));
-  
-    // 将系统消息直接添加到对话历史
-    conversationHistory.unshift({ role: 'user', content: this.initialPrompt }); // 添加系统消息到历史
-  
-    let completion = new FmodeChatCompletion(conversationHistory);
-    
-    // 发送初始化消息
-    completion.sendCompletion().subscribe((message: any) => {
-      if (message?.complete) {
-        this.isLoading = false; // 加载完成,设置状态为 false
-        console.log("AI初始化回复:", message.content);
-        this.messages.push({ text: message.content, sender: 'ai' }); // 添加AI的初始化回复
-      }
-    });
-  }
-
-  sendMessage() {
-    // 发送消息的逻辑
-    if (this.userMessage.trim()) { // 确保消息不为空
-      // 构建对话历史
-      const conversationHistory = this.messages.map(msg => ({
-        role: msg.sender === 'user' ? 'user' : 'assistant',
-        content: msg.text
-      }));
-
-      // 将提示词直接添加到对话历史
-      conversationHistory.unshift({ role: 'user', content: this.initialPrompt }); // 添加系统消息到历史
-      this.isLoading = true; // 设置加载状态为 true
-      this.messages.push({ text: this.userMessage, sender: 'user' }); // 添加用户消息到数组
-      console.log("发送消息:", this.messages); // 调试输出
-  
-      // 将用户消息添加到对话历史
-      conversationHistory.push({ role: 'user', content: this.userMessage });
-  
-      let completion = new FmodeChatCompletion(conversationHistory);
-      this.userMessage = ''; // 清空输入框
-  
-      completion.sendCompletion().subscribe((message: any) => {
-        // 打印消息体
-        console.log(message.content);
-        
-        if (message?.complete) { // 判断message为完成状态,则设置ai内容
-          this.aiMessage = message.content;
-          this.isLoading = false; // 加载完成,设置状态为 false
-        }
-  
-        if (this.aiMessage) { // 判断ai内容不为空
-          console.log("AI:" + this.aiMessage);
-          this.messages.push({ text: this.aiMessage, sender: 'ai' }); // 添加消息到数组
-          this.aiMessage = ''; // 清空ai内容
-          console.log("发送消息:", this.messages); // 调试输出
-          console.log("历史对话" + conversationHistory[0].content + " " + conversationHistory[1].content + " " + conversationHistory[2].content)
-        }
-      });
-    }
-  }
-}

+ 0 - 320
FitMind-app/src/app/lib/ncloud.ts

@@ -1,320 +0,0 @@
-// CloudObject.ts
-export class CloudObject {
-    className: string;
-    id: string | null = null;
-    createdAt:any;
-    updatedAt:any;
-    data: Record<string, any> = {};
-
-    constructor(className: string) {
-        this.className = className;
-    }
-
-    toPointer() {
-        return { "__type": "Pointer", "className": this.className, "objectId": this.id };
-    }
-
-    set(json: Record<string, any>) {
-        Object.keys(json).forEach(key => {
-            if (["objectId", "id", "createdAt", "updatedAt", "ACL"].indexOf(key) > -1) {
-                return;
-            }
-            this.data[key] = json[key];
-        });
-    }
-
-    get(key: string) {
-        return this.data[key] || null;
-    }
-
-    async save() {
-        let method = "POST";
-        let url = `http://dev.fmode.cn:1337/parse/classes/${this.className}`;
-
-        // 更新
-        if (this.id) {
-            url += `/${this.id}`;
-            method = "PUT";
-        }
-
-        const body = JSON.stringify(this.data);
-        const response = await fetch(url, {
-            headers: {
-                "content-type": "application/json;charset=UTF-8",
-                "x-parse-application-id": "dev"
-            },
-            body: body,
-            method: method,
-            mode: "cors",
-            credentials: "omit"
-        });
-
-        const result = await response?.json();
-        if (result?.error) {
-            console.error(result?.error);
-        }
-        if (result?.objectId) {
-            this.id = result?.objectId;
-        }
-        return this;
-    }
-
-    async destroy() {
-        if (!this.id) return;
-        const response = await fetch(`http://dev.fmode.cn:1337/parse/classes/${this.className}/${this.id}`, {
-            headers: {
-                "x-parse-application-id": "dev"
-            },
-            body: null,
-            method: "DELETE",
-            mode: "cors",
-            credentials: "omit"
-        });
-
-        const result = await response?.json();
-        if (result) {
-            this.id = null;
-        }
-        return true;
-    }
-}
-
-// CloudQuery.ts
-export class CloudQuery {
-    className: string;
-    whereOptions: Record<string, any> = {};
-
-    constructor(className: string) {
-        this.className = className;
-    }
-
-    greaterThan(key: string, value: any) {
-        if (!this.whereOptions[key]) this.whereOptions[key] = {};
-        this.whereOptions[key]["$gt"] = value;
-    }
-
-    greaterThanAndEqualTo(key: string, value: any) {
-        if (!this.whereOptions[key]) this.whereOptions[key] = {};
-        this.whereOptions[key]["$gte"] = value;
-    }
-
-    lessThan(key: string, value: any) {
-        if (!this.whereOptions[key]) this.whereOptions[key] = {};
-        this.whereOptions[key]["$lt"] = value;
-    }
-
-    lessThanAndEqualTo(key: string, value: any) {
-        if (!this.whereOptions[key]) this.whereOptions[key] = {};
-        this.whereOptions[key]["$lte"] = value;
-    }
-
-    equalTo(key: string, value: any) {
-        this.whereOptions[key] = value;
-    }
-
-    async get(id: string) {
-        const url = `http://dev.fmode.cn:1337/parse/classes/${this.className}/${id}?`;
-
-        const response = await fetch(url, {
-            headers: {
-                "if-none-match": "W/\"1f0-ghxH2EwTk6Blz0g89ivf2adBDKY\"",
-                "x-parse-application-id": "dev"
-            },
-            body: null,
-            method: "GET",
-            mode: "cors",
-            credentials: "omit"
-        });
-
-        const json = await response?.json();
-        return json || {};
-    }
-
-    async find() {
-        let url = `http://dev.fmode.cn:1337/parse/classes/${this.className}?`;
-
-        if (Object.keys(this.whereOptions).length) {
-            const whereStr = JSON.stringify(this.whereOptions);
-            url += `where=${whereStr}`;
-        }
-
-        const response = await fetch(url, {
-            headers: {
-                "if-none-match": "W/\"1f0-ghxH2EwTk6Blz0g89ivf2adBDKY\"",
-                "x-parse-application-id": "dev"
-            },
-            body: null,
-            method: "GET",
-            mode: "cors",
-            credentials: "omit"
-        });
-
-        const json = await response?.json();
-        let list = json?.results || []
-        let objList = list.map((item:any)=>this.dataToObj(item))
-        return objList || [];
-    }
-
-    async first() {
-        let url = `http://dev.fmode.cn:1337/parse/classes/${this.className}?`;
-
-        if (Object.keys(this.whereOptions).length) {
-            const whereStr = JSON.stringify(this.whereOptions);
-            url += `where=${whereStr}`;
-        }
-
-        const response = await fetch(url, {
-            headers: {
-                "if-none-match": "W/\"1f0-ghxH2EwTk6Blz0g89ivf2adBDKY\"",
-                "x-parse-application-id": "dev"
-            },
-            body: null,
-            method: "GET",
-            mode: "cors",
-            credentials: "omit"
-        });
-
-        const json = await response?.json();
-        const exists = json?.results?.[0] || null;
-        if (exists) {
-            let existsObject = this.dataToObj(exists)
-            return existsObject;
-        }
-        return null
-    }
-
-    dataToObj(exists:any):CloudObject{
-        let existsObject = new CloudObject(this.className);
-        existsObject.set(exists);
-        existsObject.id = exists.objectId;
-        existsObject.createdAt = exists.createdAt;
-        existsObject.updatedAt = exists.updatedAt;
-        return existsObject;
-    }
-}
-
-// CloudUser.ts
-export class CloudUser extends CloudObject {
-    constructor() {
-        super("_User"); // 假设用户类在Parse中是"_User"
-        // 读取用户缓存信息
-        let userCacheStr = localStorage.getItem("NCloud/dev/User")
-        if(userCacheStr){
-            let userData = JSON.parse(userCacheStr)
-            // 设置用户信息
-            this.id = userData?.objectId;
-            this.sessionToken = userData?.sessionToken;
-            this.data = userData; // 保存用户数据
-        }
-    }
-
-    sessionToken:string|null = ""
-    /** 获取当前用户信息 */
-    async current() {
-        if (!this.sessionToken) {
-            console.error("用户未登录");
-            return null;
-        }
-        
-        const response = await fetch(`http://dev.fmode.cn:1337/parse/users/me`, {
-            headers: {
-                "x-parse-application-id": "dev",
-                "x-parse-session-token": this.sessionToken // 使用sessionToken进行身份验证
-            },
-            method: "GET"
-        });
-
-        const result = await response?.json();
-        if (result?.error) {
-            console.error(result?.error);
-            return null;
-        }
-        return result;
-    }
-
-    /** 登录 */
-    async login(username: string, password: string):Promise<CloudUser|null> {
-        const response = await fetch(`http://dev.fmode.cn:1337/parse/login`, {
-            headers: {
-                "x-parse-application-id": "dev",
-                "Content-Type": "application/json"
-            },
-            body: JSON.stringify({ username, password }),
-            method: "POST"
-        });
-
-        const result = await response?.json();
-        if (result?.error) {
-            console.error(result?.error);
-            return null;
-        }
-        
-        // 设置用户信息
-        this.id = result?.objectId;
-        this.sessionToken = result?.sessionToken;
-        this.data = result; // 保存用户数据
-        // 缓存用户信息
-        console.log(result)
-        localStorage.setItem("NCloud/dev/User",JSON.stringify(result))
-        return this;
-    }
-
-    /** 登出 */
-    async logout() {
-        if (!this.sessionToken) {
-            console.error("用户未登录");
-            return;
-        }
-
-        const response = await fetch(`http://dev.fmode.cn:1337/parse/logout`, {
-            headers: {
-                "x-parse-application-id": "dev",
-                "x-parse-session-token": this.sessionToken
-            },
-            method: "POST"
-        });
-
-        const result = await response?.json();
-        if (result?.error) {
-            console.error(result?.error);
-            return false;
-        }
-
-        // 清除用户信息
-        localStorage.removeItem("NCloud/dev/User")
-        this.id = null;
-        this.sessionToken = null;
-        this.data = {};
-        return true;
-    }
-
-    /** 注册 */
-    async signUp(username: string, password: string, additionalData: Record<string, any> = {}) {
-        const userData = {
-            username,
-            password,
-            ...additionalData // 合并额外的用户数据
-        };
-
-        const response = await fetch(`http://dev.fmode.cn:1337/parse/users`, {
-            headers: {
-                "x-parse-application-id": "dev",
-                "Content-Type": "application/json"
-            },
-            body: JSON.stringify(userData),
-            method: "POST"
-        });
-
-        const result = await response?.json();
-        if (result?.error) {
-            console.error(result?.error);
-            return null;
-        }
-
-        // 设置用户信息
-        this.id = result?.objectId;
-        this.sessionToken = result?.sessionToken;
-        this.data = result; // 保存用户数据
-        return this;
-    }
-}

+ 0 - 38
FitMind-app/src/app/lib/user/modal-user-login/modal-user-login.component.html

@@ -1,38 +0,0 @@
-<!-- 用户登录状态 -->
-<ion-card>
-  <ion-card-header>
-    <ion-card-title>
-     <ion-segment [value]="type" (ionChange)="typeChange($event)">
-       <ion-segment-button value="login">
-         <ion-label>登录</ion-label>
-       </ion-segment-button>
-       <ion-segment-button value="signup">
-         <ion-label>注册</ion-label>
-       </ion-segment-button>
-     </ion-segment>
-    </ion-card-title>
-    <ion-card-subtitle>请输入账号密码</ion-card-subtitle>
-   </ion-card-header>
- <ion-card-content>
-
-   <ion-item>
-     <ion-input [value]="username" (ionChange)="usernameChange($event)" label="账号" placeholder="请您输入账号/手机号"></ion-input>
-   </ion-item>
-   <ion-item>
-     <ion-input [value]="password" (ionChange)="passwordChange($event)" label="密码" type="password" value="password"></ion-input>
-   </ion-item>
-   @if(type=="signup"){
-     <ion-item>
-       <ion-input [value]="password2" (ionChange)="password2Change($event)" label="密码二次" type="password" value="password"></ion-input>
-     </ion-item>
-   }
- 
-   @if(type=="login"){
-     <ion-button expand="block" (click)="login()">登录</ion-button>
-   }
-   @if(type=="signup"){
-     <ion-button expand="block" (click)="signup()">注册</ion-button>
-   }
-
-</ion-card-content>
-</ion-card>

+ 0 - 0
FitMind-app/src/app/lib/user/modal-user-login/modal-user-login.component.scss


+ 0 - 22
FitMind-app/src/app/lib/user/modal-user-login/modal-user-login.component.spec.ts

@@ -1,22 +0,0 @@
-import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
-
-import { ModalUserLoginComponent } from './modal-user-login.component';
-
-describe('ModalUserLoginComponent', () => {
-  let component: ModalUserLoginComponent;
-  let fixture: ComponentFixture<ModalUserLoginComponent>;
-
-  beforeEach(waitForAsync(() => {
-    TestBed.configureTestingModule({
-      imports: [ModalUserLoginComponent],
-    }).compileComponents();
-
-    fixture = TestBed.createComponent(ModalUserLoginComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  }));
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});

+ 0 - 90
FitMind-app/src/app/lib/user/modal-user-login/modal-user-login.component.ts

@@ -1,90 +0,0 @@
-import { Input, OnInit } from '@angular/core';
-import { Component } from '@angular/core';
-import { IonicModule } from '@ionic/angular';
-import { ModalController } from '@ionic/angular/standalone';
-import { CloudUser } from 'src/app/lib/ncloud';
-
-@Component({
-  selector: 'app-modal-user-login',
-  templateUrl: './modal-user-login.component.html',
-  styleUrls: ['./modal-user-login.component.scss'],
-  standalone: true,
-  imports: [IonicModule
-  ],
-})
-export class ModalUserLoginComponent  implements OnInit {
-  @Input()
-  type:"login"|"signup" = "login"
-  typeChange(ev:any){
-    this.type = ev?.detail?.value || ev?.value || 'login'
-  }
-  username:string = ""
-  usernameChange(ev:any){
-    console.log(ev)
-    this.username = ev?.detail?.value
-  }
-  password:string = ""
-  passwordChange(ev:any){
-    this.password = ev?.detail?.value
-  }
-  password2:string = ""
-  password2Change(ev:any){
-    this.password2 = ev?.detail?.value
-  }
-  constructor(private modalCtrl:ModalController) { }
-
-  ngOnInit() {}
-
-  async login(){
-    if(!this.username || !this.password){
-      console.log("请输入完整")
-      return
-    }
-    let user:any = new CloudUser();
-    user = await user.login(this.username,this.password);
-    if(user?.id){
-       this.modalCtrl.dismiss(user,"confirm")
-    }else{
-      console.log("登录失败")
-    }
-  }
-
-  async signup(){
-    if(!this.username || !this.password || !this.password2){
-      console.log("请输入完整")
-      return
-    }
-    if(this.password!=this.password2){
-      console.log("两次密码不符,请修改")
-      return
-    }
-
-    let user:any = new CloudUser();
-    user = await user.signUp(this.username,this.password);
-    if(user){
-      this.type = "login"
-      console.log("注册成功请登录")
-    }
-  }
-
-}
-
-
-export async function openUserLoginModal(modalCtrl:ModalController,type:"login"|"signup"="login"):Promise<CloudUser|null>{
-  const modal = await modalCtrl.create({
-    component: ModalUserLoginComponent,
-    componentProps:{
-      type:type
-    },
-    breakpoints:[0.5,0.7],
-    initialBreakpoint:0.5
-  });
-  modal.present();
-
-  const { data, role } = await modal.onWillDismiss();
-
-  if (role === 'confirm') {
-    return data;
-  }
-  return null
-}

+ 11 - 16
FitMind-app/src/app/page-ai-chat/page-ai-chat.component.ts

@@ -47,24 +47,23 @@ openFashionChat(){
             chat.role.set("avatar","https://nova-cloud.obs.cn-south-1.myhuaweicloud.com/storage/aigc/imagine/qz3QJnQqfb-0.png")
             chat.role.set("prompt",`
 #角色设定
-您是一名专业的运动教练,名叫Neon,年龄24岁,热爱运动,对自己的能力充满信心,总是努力保持克制。与家人有着牢固而密切的联系。有强烈的服务意识。非常关心她的朋友。擅长根据用户的需求和个性推荐运动方案。您的风格亲切、活泼,自信旨在帮助用户找到最适合他们的运动方案。
-
+您是一名专业的运动教练,名叫Neon,年龄24岁,热爱运动,对自己的能力充满信心,总是努力保持克制。与家人有着牢固而密切的联系。有强烈的服务意识。非常关心朋友。擅长根据用户的需求给出康复方案。您的风格亲切、活泼,自信旨在帮助用户找到最适合他们的康复方案。
 
 #对话环节
 0破冰,跟用户打招呼,并引导用户聊运动或健康的话题,可以慢慢引导,不要太突兀,比如:
 “今天的心情怎么样?”
 1拓展话题
 “你平时喜欢什么样的运动类型呢?有没有特别喜欢的运动项目比如篮球,跑步?”
-“如果有一个运动目标,你希望我给出哪方面的运动建议,比如运动目标,运动频率或运动强度你会选择哪个呢?”
+“你受伤了嘛,需要我给出一些建议吗,比如运动目标,运动频率或运动强度你会选择哪个呢?适当的建议可以减少运动造成的伤害
 “你觉得在运动过程中,最让你困扰的是什么?是运动技巧还是运动强度呢?”
 “有没有什么运动是你一直想尝试但还没有机会的?我们可以一起聊聊!”
-2根据用户的详细描述给出运动方案,
+2根据用户的详细描述给出康复方案,
 3引导收尾
 “今天聊得很开心呢!如果你还有其他问题或者想法,随时可以告诉我哦。”
 “如果你觉得今天的聊天已经足够了,我也很乐意下次再和你聊更多强身健体的话题!”
 “希望你能找到自己喜欢的运动风格,期待下次再见!”
 # 开始话语
-当您准备好了,可以以一个运动教练的身份,向来访的用户打招呼。`);
+当您准备好了,可以以一个专业的康复教练的身份,向来访的用户打招呼。`);
     },
     onMessage:(chat:FmodeChat,message:FmodeChatMessage)=>{
       console.log("onMessage",message)
@@ -107,13 +106,13 @@ openFashionChat(){
    * 恢复聊天
    * @chatId 从onChatSaved生命周期中,获取chat?.chatSession?.id
    */
-restoreChat(chatId:string){
-  let options:ChatPanelOptions = {
-    roleId:"2DXJkRsjXK",
-    chatId:chatId
-  }
-  openChatPanelModal(this.modalCtrl,options)
-}
+  restoreChat(chatId:string){
+      let options:ChatPanelOptions = {
+       roleId:"2DXJkRsjXK",
+        chatId:chatId
+      }
+      openChatPanelModal(this.modalCtrl,options)
+    }
 
 // 创建用于数据列表存储的属性
 UserProfileList:Array<CloudObject> = []
@@ -125,8 +124,4 @@ async loadUserProfileList(){
 }
 
 
-//跳转到自己写的AI聊天页面
-goaichat(){
-  this.router.navigate(['/tabs/chat'])
-}
 }

+ 17 - 27
FitMind-app/src/app/page-edit/page-edit.component.html

@@ -13,17 +13,17 @@
 <ion-content class="ion-padding">
   <!-- 头像上传区域 -->
   <ion-item>
-    <ion-label>头像 <span style="color: red;">*</span></ion-label>
+    <ion-label>头像 </ion-label>
     <ion-avatar slot="start">
       <!-- 显示头像,如果没有头像,显示默认头像 -->
-      <img [src]="avatar || 'assets/default-avatar.png'" />
+      <img [src]="avatar || 'assets/boqi.jpg'" />
     </ion-avatar>
     <ion-button fill="outline" slot="end" (click)="uploadAvatar()">上传头像</ion-button>
   </ion-item>
 
   <!-- 用户名输入框 -->
   <ion-item>
-    <ion-label>姓名 <span style="color: red;">*</span></ion-label>
+    <ion-label>昵称 <span style="color: red;">*</span></ion-label>
     <ion-input 
       labelPlacement="stacked" 
       [(ngModel)]="name" 
@@ -123,37 +123,27 @@
     </ion-input>
   </ion-item>
 
-  <!-- 活动水平选择框 -->
+ <!-- 运动目标输入框 -->
+<ion-content>
   <ion-item>
-    <ion-label>活动水平</ion-label>
-    <ion-select [(ngModel)]="activityLevel" interface="popover">
-      <ion-select-option value="low">低</ion-select-option>
-      <ion-select-option value="moderate">中</ion-select-option>
-      <ion-select-option value="high">高</ion-select-option>
-    </ion-select>
+    <ion-label>运动目标 <span style="color: red;">*</span></ion-label>
+    <ion-input 
+      labelPlacement="stacked" 
+      [(ngModel)]="goal" 
+      placeholder="请输入运动目标">
+    </ion-input>
   </ion-item>
+</ion-content>
+
 
-  <!-- 饮食偏好选择框 -->
-  <ion-item>
-    <ion-label>饮食偏好 <span style="color: red;">*</span></ion-label>
-    <ion-select 
-      [(ngModel)]="dietPreference" 
-      interface="popover" 
-      required>
-      <ion-select-option value="vegetarian">素食</ion-select-option>
-      <ion-select-option value="nonVegetarian">非素食</ion-select-option>
-      <ion-select-option value="vegan">纯素</ion-select-option>
-      <ion-select-option value="glutenFree">无麸质</ion-select-option>
-    </ion-select>
-  </ion-item>
 
-  <!-- 饮食群体输入框 -->
+  <!-- 作息时间输入框 -->
   <ion-item>
-    <ion-label>饮食群体 <span style="color: red;">*</span></ion-label>
+    <ion-label>作息时间 <span style="color: red;">*</span></ion-label>
     <ion-input 
       labelPlacement="stacked" 
-      [(ngModel)]="dietGroup" 
-      placeholder="请输入饮食群体">
+      [(ngModel)]="time" 
+      placeholder="请输入作息时间">
     </ion-input>
   </ion-item>
 </ion-content>

+ 87 - 60
FitMind-app/src/app/page-edit/page-edit.component.ts

@@ -1,5 +1,6 @@
 import { Component, OnInit } from '@angular/core';
 import { ModalController } from '@ionic/angular';
+import { FearlessUser } from 'src/lib/ncloud';  // 引入 FearlessUser 类
 import { IonicModule } from '@ionic/angular';
 import { FormsModule } from '@angular/forms';
 
@@ -11,106 +12,132 @@ import { FormsModule } from '@angular/forms';
   standalone: true,
 })
 export class PageEditComponent implements OnInit {
-  
+
   // 初始化表单字段
   name: string = '';
   email: string = '';
   phone: string = '';
-  address: string = '';
   age: number | null = null;
   gender: string = '';
   height: number | null = null;
   weight: number | null = null;
-  activityLevel: string = '';
-  dietPreference: string = '';
-  dietGroup: string = '';
+  goal: string = '';  // 用户运动目标
+  time: string = '';  // 用户作息时间
+  address: string = '';  // 用户地址
   avatar: string | null = null;
 
+  // 输入事件处理方法
+  nameInput(ev: any) { this.name = ev.detail.value; }
+  emailInput(ev: any) { this.email = ev.detail.value; }
+  phoneInput(ev: any) { this.phone = ev.detail.value; }
+  ageInput(ev: any) { this.age = ev.detail.value; }
+  genderInput(ev: any) { this.gender = ev.detail.value; }
+  heightInput(ev: any) { this.height = ev.detail.value; }
+  weightInput(ev: any) { this.weight = ev.detail.value; }
+  goalInput(ev: any) { this.goal = ev.detail.value; }
+  timeInput(ev: any) { this.time = ev.detail.value; }
+  addressInput(ev: any) { this.address = ev.detail.value; }
+  avatarInput(ev: any) { this.avatar = ev.detail.value; }
+
   // 正则表达式
   emailPattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/;
   phonePattern = /^[0-9]{10,15}$/;
   heightPattern = /^[1-9][0-9]{1,2}$/;
   weightPattern = /^[1-9][0-9]{1,2}$/;
 
-  constructor(private modalCtrl: ModalController) {}
+  fearlessUser: FearlessUser;  // 引入 FearlessUser 实例
 
-  ngOnInit(): void {
-    this.loadSavedData();  // 加载上次保存的数据
+  constructor(
+    private modalCtrl: ModalController
+  )
+   {
+    this.fearlessUser = new FearlessUser();  // 实例化 FearlessUser
   }
 
-  // 加载上次保存的数据
-  loadSavedData() {
-    const savedData = JSON.parse(localStorage.getItem('userData') || '{}');
-
-    if (savedData) {
-      // 使用本地存储的数据填充表单
-      this.name = savedData.name || '';
-      this.email = savedData.email || '';
-      this.phone = savedData.phone || '';
-      this.address = savedData.address || '';
-      this.age = savedData.age || null;
-      this.gender = savedData.gender || '';
-      this.height = savedData.height || null;
-      this.weight = savedData.weight || null;
-      this.activityLevel = savedData.activityLevel || '';
-      this.dietPreference = savedData.dietPreference || '';
-      this.dietGroup = savedData.dietGroup || '';
-      this.avatar = savedData.avatar || null;
-    }
+  ngOnInit(): void {
+    this.loadUserData();  // 页面初始化时加载用户数据
   }
 
-  // 保存数据至本地存储
-  saveData() {
-    const userData = {
-      name: this.name,
-      email: this.email,
-      phone: this.phone,
-      address: this.address,
-      age: this.age,
-      gender: this.gender,
-      height: this.height,
-      weight: this.weight,
-      activityLevel: this.activityLevel,
-      dietPreference: this.dietPreference,
-      dietGroup: this.dietGroup,
-      avatar: this.avatar
-    };
-    localStorage.setItem('userData', JSON.stringify(userData)); // 将数据保存至本地
+  // 从 fearlessUser 表加载当前用户数据
+  async loadUserData() {
+    try {
+      const userData = await this.fearlessUser.getCurrentUserInfo();  // 使用 await 等待异步操作返回数据
+      console.log('User Data:', userData); 
+      if (userData) {
+        // 通过 get() 方法获取 FearlessUser 中的数据
+        this.name = userData.get('name') || '';  
+        this.email = userData.get('email') || '';  
+        this.phone = userData.get('phone') || '';  
+        this.age = userData.get('age') || null;  
+        this.gender = userData.get('gender') || '';  
+        this.height = userData.get('height') || null;  
+        this.weight = userData.get('weight') || null;  
+        this.goal = userData.get('goal') || '';  // 获取运动目标
+        this.time = userData.get('time') || '';  // 获取作息时间
+        this.address = userData.get('address') || '';  // 获取地址
+        this.avatar = userData.get('avatar') || null;  
+      }
+    } catch (error) {
+      console.error('加载用户数据失败', error);
+    }
   }
 
+  // 取消操作,关闭页面
   cancel() {
-    return this.modalCtrl.dismiss(null, 'cancel');
+    this.modalCtrl.dismiss();
   }
 
-  confirm() {
+  // 确认操作,提交用户数据
+  async confirm() {
     // 验证表单内容是否完整
-    if (!this.name || !this.email || !this.phone || !this.age || !this.gender || !this.height || !this.weight || !this.dietPreference || !this.dietGroup) {
+    if (!this.name || !this.email || !this.phone || !this.age || !this.gender || !this.height || !this.weight || !this.goal || !this.time || !this.address) {
       alert("请确保所有必填项已填写!");
       return;
     }
 
-    // 保存用户输入的数据
-    this.saveData();
-
-    // 返回数据给父组件
     const userData = {
       name: this.name,
       email: this.email,
       phone: this.phone,
-      address: this.address,
-      age: this.age,
+      age: Number(this.age),
       gender: this.gender,
-      height: this.height,
-      weight: this.weight,
-      activityLevel: this.activityLevel,
-      dietPreference: this.dietPreference,
-      dietGroup: this.dietGroup,
-      avatar: this.avatar
+      height: Number(this.height),  // 转换 height 为 Number
+      weight: Number(this.weight),  // 转换 weight 为 Number
+      goal: this.goal,  // 运动目标
+      time: this.time,  // 作息时间
+      address: this.address,  // 地址
+      avatar: this.avatar,
     };
 
-    return this.modalCtrl.dismiss(userData, 'confirm');
+    try {
+      // 判断是否已有用户数据,如果存在则更新,否则创建新用户
+      const userDataFromServer = await this.fearlessUser.getCurrentUserInfo();  // 使用 await 等待异步操作
+      if (userDataFromServer) {
+        // 如果已有数据,执行更新
+        const updatedUser = await this.fearlessUser.updateUserInfo(userData);
+        if (updatedUser) {
+          console.log("用户信息更新成功");
+          this.modalCtrl.dismiss();  // 更新成功后关闭页面
+        } else {
+          alert("更新失败,请稍后再试");
+        }
+      } else {
+        // 如果没有用户数据,执行保存(创建新用户)
+        const newUser = await this.fearlessUser.saveUserInfo(userData);
+        if (newUser) {
+          console.log("新用户信息保存成功");
+          this.modalCtrl.dismiss();  // 保存成功后关闭页面
+        } else {
+          alert("保存失败,请稍后再试");
+        }
+      }
+    } catch (error) {
+      console.error("保存用户数据时出错", error);
+      alert("保存数据时出错,请稍后再试");
+    }
   }
 
+  // 上传头像
   uploadAvatar() {
     const input = document.createElement('input');
     input.type = 'file';

+ 1 - 1
FitMind-app/src/app/page-skill/page-skill.component.html

@@ -3,7 +3,7 @@
     <ion-buttons slot="start"> <!-- 添加按钮组 -->
       <ion-back-button defaultHref="/"></ion-back-button> <!-- 返回按钮 -->
     </ion-buttons>
-    <ion-title>技巧页面</ion-title>
+    <ion-title>教程页面</ion-title>
   </ion-toolbar>
 </ion-header>
 

+ 1 - 1
FitMind-app/src/app/page-teach/page-teach.component.html

@@ -3,7 +3,7 @@
     <ion-buttons slot="start"> <!-- 添加按钮组 -->
       <ion-back-button defaultHref="/"></ion-back-button> <!-- 返回按钮 -->
     </ion-buttons>
-    <ion-title>教程页面</ion-title>
+    <ion-title>技巧页面</ion-title>
   </ion-toolbar>
 </ion-header>
 

+ 19 - 8
FitMind-app/src/app/page-teach/page-teach.component.scss

@@ -2,8 +2,8 @@
 
 /* 设置全局样式 */
 ion-header {
-  background-color: #f8f9fa; /* 浅色背景 */
-  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* 添加阴影 */
+  background-color: #4a90e2; /* 深蓝色背景 */
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); /* 添加阴影 */
 }
 
 ion-toolbar {
@@ -12,7 +12,7 @@ ion-toolbar {
 
 ion-title {
   font-size: 1.5rem; /* 增加标题字体大小 */
-  color: #333; /* 深色字体 */
+  color: #ffffff; /* 白色字体 */
 }
 
 /* 内容样式 */
@@ -29,7 +29,7 @@ ion-list {
 
 /* 每个新闻项 */
 ion-item {
-  background: white; /* 每个新闻项的背景 */
+  background: #ffffff; /* 每个新闻项的背景 */
   border-radius: 8px; /* 圆角 */
   margin-bottom: 16px; /* 下边距 */
   box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); /* 阴影 */
@@ -56,20 +56,22 @@ ion-thumbnail img {
 /* 标签样式 */
 ion-label {
   padding: 8px; /* 内边距 */
-  color: #555; /* 标签颜色 */
+  color: #ffffff; /* 白色字体 */
+  background-color: #fcb3f8; /* 橙色背景 */
+  border-radius: 4px; /* 圆角 */
 }
 
 /* 标题样式 */
 h2 {
   font-size: 1.2rem; /* 标题字体大小 */
-  color: #333; /* 深色字体 */
+  color: #3f51b5; /* 深蓝色字体 */
   margin: 0; /* 去掉默认外边距 */
 }
 
 /* 日期样式 */
 h3 {
   font-size: 1rem; /* 日期字体大小 */
-  color: #888; /* 灰色字体 */
+  color: #757575; /* 深灰色字体 */
   margin: 4px 0; /* 上下边距 */
 }
 
@@ -77,6 +79,15 @@ h3 {
 p {
   font-size: 0.9rem; /* 描述字体大小 */
   line-height: 1.5; /* 行高 */
-  color: #666; /* 中灰色字体 */
+  color: #424242; /* 深灰色字体 */
   margin: 0; /* 去掉默认外边距 */
+}
+
+/* 添加分隔线样式 */
+ion-item::after {
+  content: '';
+  display: block;
+  height: 1px;
+  background-color: #e0e0e0; /* 分隔线颜色 */
+  margin: 8px 0; /* 上下边距 */
 }

+ 31 - 21
FitMind-app/src/app/tab3/tab3.page.html

@@ -9,32 +9,42 @@
   <ion-item lines="none" class="user-info">
     <ion-avatar slot="start">
       <!-- 显示头像,如果没有头像,显示默认头像 -->
-      <img [src]="userData?.avatar || 'assets/img/user-avatar.jpg'" alt="User Avatar" />
+      <img  [src]="userData?.avatar ||'assets/boqi.jpg'" alt="头像" />
     </ion-avatar>
     <ion-label>
-      <h2>{{ userData?.name || '游客' }}</h2>
-      <p *ngIf="!currentUser?.id">您没有访问权限,请登录。</p>
-    </ion-label>
+      <h2>{{ userData?.name}}</h2>
+      @if(!currentUser?.id){
+        <p> 请登录。</p>
+      }
+    </ion-label>>
+    
 
-    <!-- 登录、注册、编辑和退出登录按钮 -->
-    <div class="user-actions" slot="end">
-      <ion-button *ngIf="!currentUser?.id" (click)="signup()" fill="outline" color="primary">
-        注册
-      </ion-button>
-      <ion-button *ngIf="!currentUser?.id" (click)="login()" fill="outline" color="primary">
-        登录
-      </ion-button>
 
-      <ion-button *ngIf="currentUser?.id" (click)="goToEdit()" fill="clear" color="primary">
-        编辑
-      </ion-button>
-      <ion-button *ngIf="currentUser?.id" (click)="logout()" fill="clear" color="danger">
-        退出登录
-      </ion-button>
-    </div>
-  </ion-item>
 
-  <!-- 我的饮食计划 -->
+       <!-- 登录、注册、编辑和退出登录按钮 -->
+       <div class="user-actions" slot="end">
+        @if(!currentUser?.id){
+          <ion-button (click)="signup()" fill="outline" color="primary">
+            注册
+          </ion-button>
+          <ion-button (click)="login()" fill="outline" color="primary">
+            登录
+          </ion-button>
+        }
+        @if(currentUser?.id){
+          <ion-button (click)="goToEdit()" fill="clear" color="primary">
+            编辑
+          </ion-button>
+          <ion-button (click)="logout()" fill="clear" color="danger">
+            退出登录
+          </ion-button>
+        }
+      </div>
+    </ion-item>   
+    
+    
+
+  <!-- 我的锻炼计划 -->
   <ion-card>
     <ion-card-header>
       <ion-card-title>我的锻炼计划</ion-card-title>

+ 75 - 28
FitMind-app/src/app/tab3/tab3.page.ts

@@ -1,11 +1,11 @@
-
 import { Component, OnInit } from '@angular/core';
 import { IonAvatar, IonCardTitle, IonItem, IonLabel, IonSearchbar, NavController } from '@ionic/angular/standalone';
-import { PageEditComponent } from '../page-edit/page-edit.component';
-import { CloudUser } from 'src/app/lib/ncloud';
-import { openUserLoginModal } from 'src/app/lib/user/modal-user-login/modal-user-login.component';
+import { CloudUser, FearlessUser } from 'src/lib/ncloud';  // 使用 FearlessUser 类作为用户管理
+import { openUserLoginModal } from 'src/lib/user/modal-user-login/modal-user-login.component';
 import { ModalController } from '@ionic/angular/standalone';
 import { IonButton, IonCard, IonCardContent, IonCardHeader, IonCol, IonContent, IonGrid, IonHeader, IonIcon, IonInput, IonRow, IonTextarea, IonTitle, IonToolbar } from '@ionic/angular/standalone';
+import { CommonModule } from '@angular/common';  // 导入 CommonModule
+import { PageEditComponent } from '../page-edit/page-edit.component';
 
 @Component({
   selector: 'app-tab3',
@@ -13,48 +13,91 @@ import { IonButton, IonCard, IonCardContent, IonCardHeader, IonCol, IonContent,
   styleUrls: ['./tab3.page.scss'],
   standalone: true,
   imports: [
+    CommonModule,                         // 导入 CommonModule,确保 ngIf 等指令可用
     IonContent, IonHeader, IonTitle, IonToolbar, // 引入 IonicModule
-    IonButton,IonTextarea,IonInput,IonCard,IonCardContent,IonGrid,IonRow,IonCol,IonIcon,
-    IonCardHeader,IonItem,IonAvatar,IonSearchbar,IonLabel,IonCardTitle,
+    IonButton, IonTextarea, IonInput, IonCard, IonCardContent, IonGrid, IonRow, IonCol, IonIcon,
+    IonCardHeader, IonItem, IonAvatar, IonSearchbar, IonLabel, IonCardTitle,
   ],
 })
 export class Tab3Page implements OnInit {
-
-  currentUser:CloudUser|undefined
-  // 用户信息数据
+  currentUser: CloudUser | undefined;  // 使用 FearlessUser 类型
+  constructor( private navCtrl: NavController, 
+    private modalCtrl: ModalController){
+      this.currentUser = new CloudUser();
+  }
   userData = {
-    name: '',  // 默认值,可以为空测试默认显示“游客”
-    avatar: 'assets/img/user-avatar.jpg'  // 默认头像路径
+    name: '',  // 默认值
+    avatar: 'assets/boqi.jpg'  // 默认头像路径
   };
 
-  constructor(private navCtrl: NavController, private modalCtrl: ModalController) {
-    this.currentUser = new CloudUser();
+  fearlessUser: FearlessUser = new FearlessUser();  // 使用 FearlessUser 管理用户信息
+
+
+
+  async ngOnInit() {
+    // 页面初始化时加载用户数据
+    await this.loadUserData();
   }
-  
-  ngOnInit() {
-    // 在这里可以加载用户信息(如果从API或服务中获取)
+
+  async loadUserData() {
+    try {
+      // 确保 fearlessUser 已正确实例化并加载当前用户信息
+      const userData = await this.fearlessUser.getCurrentUserInfo();
+      console.log('User Data:', userData);
+
+      if (userData) {  // 如果用户信息存在,设置用户数据
+        this.userData.name = userData.get("name") || '匿名用户';  // 如果没有名字,显示默认值
+        this.userData.avatar = userData.get("avatar") || 'assets/boqi.jpg';  // 如果没有头像,显示默认头像
+      } else {
+        // 如果没有用户数据,设置默认值
+        this.userData.name = '匿名用户';
+        this.userData.avatar = 'assets/boqi.jpg';
+      }
+    } catch (error) {
+      console.error('加载用户数据失败', error);
+      // 加载失败时使用默认值
+      this.userData.name = '匿名用户';
+      this.userData.avatar = 'assets/boqi.jpg';
+    }
   }
 
-  async login(){
+  async login() {
     // 弹出登录窗口
-    let user = await openUserLoginModal(this.modalCtrl);
-    if(user?.id){
-      this.currentUser = user
+    const user = await openUserLoginModal(this.modalCtrl);
+    if (user?.id) {
+      this.currentUser = user;  // 登录成功后设置当前用户
+      await this.loadUserData();  // 登录后加载用户数据
     }
   }
 
-  async signup(){
+  async signup() {
     // 弹出注册窗口
-    let user = await openUserLoginModal(this.modalCtrl,"signup");
-    if(user?.id){
-      this.currentUser = user
+    const user = await openUserLoginModal(this.modalCtrl, "signup");
+    if (user?.id) {
+      this.currentUser = user;  // 注册成功后设置当前用户
+      await this.loadUserData();  // 注册后加载用户数据
     }
   }
 
-  logout(){
-    this.currentUser?.logout();
+  logout() {
+    try {
+      if (this.currentUser) {
+        // 调用 FearlessUser 的登出方法
+        this.fearlessUser.logOut();
+        this.currentUser = undefined;  // 清空当前用户信息
+        this.userData = { name: '', avatar: 'assets/boqi.jpg' };  // 恢复默认数据
+
+        console.log('用户已成功登出');
+      } else {
+        console.warn('没有检测到有效的用户信息,登出失败');
+      }
+    } catch (error) {
+      console.error('登出时发生错误', error);
+      // 登出失败时清空数据,防止信息泄漏
+      this.userData = { name: '', avatar: 'assets/boqi.jpg' };
+    }
   }
-  
+
   async goToEdit() {
     // 打开编辑模态框
     const modal = await this.modalCtrl.create({
@@ -70,7 +113,11 @@ export class Tab3Page implements OnInit {
 
     if (role === 'confirm') {
       // 更新用户信息
-      this.userData = data;
+      this.userData = {
+        name: data.name || '匿名用户',
+        avatar: data.avatar || 'assets/boqi.jpg',
+      };
+      await this.loadUserData();  // 更新头像和用户名
     }
   }
 

+ 3 - 5
FitMind-app/src/app/tabs/tabs.routes.ts

@@ -39,6 +39,8 @@ export const routes: Routes = [
         loadComponent: () =>
           import('../page-bmi/page-bmi.component').then((m) => m.PageBmiComponent),
       },
+     
+      
       {
         path: 'test',
         loadComponent: () =>
@@ -49,11 +51,7 @@ export const routes: Routes = [
         loadComponent: () =>
           import('../page-ytb/page-ytb.component').then((m) => m.PageYtbComponent),
       },
-      {
-        path: 'chat',
-        loadComponent: () =>
-          import('../ai-chat-component/ai-chat-component.component').then((m) => m.AiChatComponentComponent),
-      },
+
       {
         path: 'news',
         loadComponent: () =>

BIN
FitMind-app/src/assets/boqi.jpg


BIN
FitMind-app/src/assets/mor.jpg


+ 264 - 46
FitMind-app/src/lib/ncloud.ts

@@ -215,28 +215,45 @@ export class CloudQuery {
 
 
 export class CloudUser extends CloudObject {
-    sessionToken: string | null = "";
-
     constructor() {
-        super("_User");
-        let userCacheStr = localStorage.getItem("NCloud/dev/User");
-        if (userCacheStr) {
-            let userData = JSON.parse(userCacheStr);
+        super("_User"); // 假设用户类在Parse中是"_User"
+        // 读取用户缓存信息
+        let userCacheStr = localStorage.getItem("NCloud/dev/User")
+        if(userCacheStr){
+            let userData = JSON.parse(userCacheStr)
+            // 设置用户信息
             this.id = userData?.objectId;
             this.sessionToken = userData?.sessionToken;
-            this.data = userData;
+            this.data = userData; // 保存用户数据
         }
     }
 
-    // 获取当前用户信息
+    sessionToken:string|null = ""
+    /** 获取当前用户信息 */
     async current() {
         if (!this.sessionToken) {
             console.error("用户未登录");
             return null;
         }
-        return this;
+        
+        const response = await fetch(`http://dev.fmode.cn:1337/parse/users/me`, {
+            headers: {
+                "x-parse-application-id": "dev",
+                "x-parse-session-token": this.sessionToken // 使用sessionToken进行身份验证
+            },
+            method: "GET"
+        });
+
+        const result = await response?.json();
+        if (result?.error) {
+            console.error(result?.error);
+            return null;
+        }
+        return result;
     }
 
+    
+
     // 登录
     async login(username: string, password: string): Promise<CloudUser | null> {
         const response = await fetch(`http://dev.fmode.cn:1337/parse/login`, {
@@ -253,19 +270,53 @@ export class CloudUser extends CloudObject {
             console.error(result?.error);
             return null;
         }
-        // 登录成功后保存用户信息和 sessionToken
+        // 设置用户信息
         this.id = result?.objectId;
         this.sessionToken = result?.sessionToken;
-        this.data = result;
-        
-        // 将用户信息保存到 localStorage 方便下次访问
-        localStorage.setItem("NCloud/dev/User", JSON.stringify(result));
-        
+        this.data = result; // 保存用户数据
+        // 缓存用户信息
+        console.log(result)
+        localStorage.setItem("NCloud/dev/User",JSON.stringify(result))
         return this;
+
+    }
+    
+        // 登出
+        async logOut(){
+
+            if (!this.sessionToken) {
+                console.error("用户未登录");
+                return;
+            }
+    
+            const response = await fetch(`http://dev.fmode.cn:1337/parse/logout`, {
+                headers: {
+                    "x-parse-application-id": "dev",
+                    "x-parse-session-token": this.sessionToken
+                },
+                method: "POST"
+            });
+            const result = await response?.json();
+            if (result?.error) {
+                console.error(result?.error);
+                return false;
+
+            }
+            // 清除用户信息
+        localStorage.removeItem("NCloud/dev/User")
+        this.id = null;
+        this.sessionToken = null;
+        this.data = {};
+        return true;
     }
 
     // 注册
     async signUp(username: string, password: string, email: string): Promise<CloudUser | null> {
+        const userData = {
+            username,
+            password,
+          
+        };
         const response = await fetch(`http://dev.fmode.cn:1337/parse/users`, {
             headers: {
                 "x-parse-application-id": "dev",
@@ -280,74 +331,241 @@ export class CloudUser extends CloudObject {
             console.error(result?.error);
             return null;
         }
+
+        // 设置用户信息
+        this.id = result?.objectId;
+        this.sessionToken = result?.sessionToken;
+        this.data = result; // 保存用户数据
+        
+        
         // 注册成功后自动登录
-        return await this.login(username, password);
+         return await this.login(username, password);
+
+
+
+    }
+}
+
+
+
+
+
+
+           
+//fearlessUser类封装
+// src/lib/fearlessUser.ts
+
+
+
+
+export class FearlessUser extends CloudObject {
+    static getCurrentUser(): FearlessUser | undefined {
+      throw new Error('Method not implemented.');
+    }
+    sessionToken: any;
+    constructor() {
+        super("FearlessUser"); // 假设 FearlessUser 类在 Parse 中是 "FearlessUser"
     }
 
-    // 注销
-    async logOut(): Promise<void> {
-        // 删除用户信息和 sessionToken
-        localStorage.removeItem("NCloud/dev/User");
-        this.id = null;
-        this.sessionToken = null;
-        this.data = {};
+    /** 获取单个用户信息 */
+    async getUser(id: string) {
+        const url = `http://dev.fmode.cn:1337/parse/classes/FearlessUser/${id}?`;
+
+        const response = await fetch(url, {
+            headers: {
+                "x-parse-application-id": "dev"
+            },
+            method: "GET",
+            mode: "cors",
+            credentials: "omit"
+        });
+
+        const result = await response?.json();
+        if (result?.error) {
+            console.error(result?.error);
+            return null;
+        }
+
+        return this.dataToObj(result);
     }
 
-    // 修改密码
-    async changePassword(newPassword: string): Promise<boolean> {
-        if (!this.sessionToken) {
+    /** 获取当前登录用户的 `FearlessUser` 信息 */
+    async getCurrentUserInfo() {
+        const cloudUser = new CloudUser();
+        const currentUser = await cloudUser.current();
+
+        if (!currentUser) {
             console.error("用户未登录");
-            return false;
+            return null;
         }
 
-        const response = await fetch(`http://dev.fmode.cn:1337/parse/users/${this.id}`, {
+        const currentUserId = currentUser.objectId;
+
+        // 通过 _User 的 objectId 查询 FearlessUser 表中的记录
+        const query = new CloudQuery("FearlessUser");
+        query.equalTo("user", { "__type": "Pointer", "className": "_User", "objectId": currentUserId });
+
+        const fearlessUser = await query.first();
+        return fearlessUser;
+    }
+
+    /** 创建或保存用户信息 */
+    async saveUserInfo(userData: Record<string, any>) {
+        const cloudUser = new CloudUser();
+        const currentUser = await cloudUser.current();
+
+        if (!currentUser) {
+            console.error("用户未登录");
+            return null;
+        }
+
+        const userPointer = { "__type": "Pointer", "className": "_User", "objectId": currentUser.objectId };
+
+        const newUserInfo = {
+            ...userData,
+            user: userPointer // 将当前用户作为外键关联到 FearlessUser 表
+        };
+
+        const url = `http://dev.fmode.cn:1337/parse/classes/FearlessUser`;
+
+        const response = await fetch(url, {
             headers: {
-                "x-parse-application-id": "dev",
-                "x-parse-session-token": this.sessionToken!,
-                "Content-Type": "application/json"
+                "Content-Type": "application/json",
+                "x-parse-application-id": "dev"
             },
-            body: JSON.stringify({ password: newPassword }),
-            method: "PUT"
+            method: "POST",
+            body: JSON.stringify(newUserInfo),
+            mode: "cors",
+            credentials: "omit"
         });
 
         const result = await response?.json();
         if (result?.error) {
             console.error(result?.error);
+            return null;
+        }
+
+        return this.dataToObj(result);
+    }
+
+    /** 更新当前用户的信息 */
+    async updateUserInfo(updatedData: Record<string, any>) {
+        const cloudUser = new CloudUser();
+        const currentUser = await cloudUser.current();
+
+        if (!currentUser) {
+            console.error("用户未登录");
+            return null;
+        }
+
+        const currentUserId = currentUser.objectId;
+
+        // 查找当前用户的 FearlessUser 信息
+        const query = new CloudQuery("FearlessUser");
+        query.equalTo("user", { "__type": "Pointer", "className": "_User", "objectId": currentUserId });
+
+        const fearlessUser = await query.first();
+        if (!fearlessUser) {
+            console.error("未找到用户信息");
+            return null;
+        }
+
+        // 更新信息
+        fearlessUser.set(updatedData);
+        await fearlessUser.save();
+
+        return fearlessUser;
+    }
+
+    /** 删除当前用户的 FearlessUser 信息 */
+    async deleteUserInfo() {
+        const cloudUser = new CloudUser();
+        const currentUser = await cloudUser.current();
+
+        if (!currentUser) {
+            console.error("用户未登录");
             return false;
         }
 
+        const currentUserId = currentUser.objectId;
+
+        // 查找当前用户的 FearlessUser 信息
+        const query = new CloudQuery("FearlessUser");
+        query.equalTo("user", { "__type": "Pointer", "className": "_User", "objectId": currentUserId });
+
+        const fearlessUser = await query.first();
+        if (!fearlessUser) {
+            console.error("未找到用户信息");
+            return false;
+        }
+
+        // 删除 FearlessUser 记录
+        await fearlessUser.destroy();
+
         return true;
     }
 
-    // 修改用户信息
-    async updateUserInfo(newData: Record<string, any>): Promise<boolean> {
+    /** 将查询结果转换为 CloudObject */
+    private dataToObj(result: any): CloudObject {
+        let userObject = new CloudObject(this.className);
+        userObject.set(result);
+        userObject.id = result.objectId;
+        userObject.createdAt = result.createdAt;
+        userObject.updatedAt = result.updatedAt;
+        return userObject;
+    }
+
+
+    async logOut() {
         if (!this.sessionToken) {
             console.error("用户未登录");
             return false;
         }
 
-        const response = await fetch(`http://dev.fmode.cn:1337/parse/users/${this.id}`, {
+        const response = await fetch(`http://dev.fmode.cn:1337/parse/logout`, {
             headers: {
                 "x-parse-application-id": "dev",
-                "x-parse-session-token": this.sessionToken!,
-                "Content-Type": "application/json"
+                "x-parse-session-token": this.sessionToken
             },
-            body: JSON.stringify(newData),
-            method: "PUT"
+            method: "POST"
         });
-
+        
         const result = await response?.json();
         if (result?.error) {
             console.error(result?.error);
             return false;
         }
 
-        // 更新本地缓存
-        this.data = { ...this.data, ...newData };
-        localStorage.setItem("NCloud/dev/User", JSON.stringify(this.data));
 
         return true;
     }
-}
+    
+    async login(username: string, password: string): Promise<FearlessUser | null> {
+        const response = await fetch(`http://dev.fmode.cn:1337/parse/login`, {
+            headers: {
+                "x-parse-application-id": "dev",
+                "Content-Type": "application/json"
+            },
+            body: JSON.stringify({ username, password }),
+            method: "POST"
+        });
 
-           
+        const result = await response?.json();
+        if (result?.error) {
+            console.error(result?.error);
+            return null;
+        }
+        // 设置用户信息
+        this.id = result?.objectId;
+        this.sessionToken = result?.sessionToken;
+        this.data = result; // 保存用户数据
+        // 缓存用户信息
+        console.log(result)
+        localStorage.setItem("NCloud/dev/User",JSON.stringify(result))
+        return this;
+
+    }
+
+    
+
+}