0225273 14 小时之前
父节点
当前提交
8b59f2fe4a

+ 2 - 0
MindOCApp/src/app/ai/ai.module.ts

@@ -6,6 +6,8 @@ import { AiPage } from './ai.page';
 import { ExploreContainerComponentModule } from '../explore-container/explore-container.module';
 
 import { AiPageRoutingModule } from './ai-routing.module';
+// 1. 导入 HttpClientModule
+import { HttpClientModule } from '@angular/common/http';
 
 @NgModule({
   imports: [

+ 60 - 15
MindOCApp/src/app/ai/ai.page.html

@@ -1,17 +1,62 @@
-<ion-header [translucent]="true">
-  <ion-toolbar>
-    <ion-title>
-      Tab 3
-    </ion-title>
-  </ion-toolbar>
-</ion-header>
+<ion-content>
+  <div>
+    Tab 3
+  </div>
+  <input type="text" placeholder="Write here..." name="text" class="input">
+</ion-content>
 
-<ion-content [fullscreen]="true">
-  <ion-header collapse="condense">
-    <ion-toolbar>
-      <ion-title size="large">Tab 3</ion-title>
-    </ion-toolbar>
-  </ion-header>
 
-  <app-explore-container name="Tab 3 page"></app-explore-container>
-</ion-content>
+<!-- ai.page.html
+<ion-content class="ai-guard">
+  动态情绪仪表
+  <div class="emotion-meter">
+    <div class="wave"></div>
+    <div class="face-avatar">
+      <img [src]="currentMood.avatar" alt="AI头像">
+    </div>
+  </div>
+
+  对话容器
+  <div class="chat-container">
+    <div #messageContainer class="messages">
+      AI消息
+      <div *ngFor="let msg of chatHistory" class="message" 
+           [class.ai]="msg.isAI"
+           [@messageAnim]>
+        <div class="bubble">
+          <div class="content">{{ msg.content }}</div>
+          <div class="meta">
+            <span class="time">{{ msg.time | date:'HH:mm' }}</span>
+            <ion-icon *ngIf="msg.suggestion" name="bulb"></ion-icon>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    输入区域
+    <div class="input-area">
+      <ion-textarea [(ngModel)]="userInput" 
+                    placeholder="和心灵伙伴聊聊..." 
+                    autoGrow
+                    (keyup.enter)="sendMessage()"></ion-textarea>
+      <ion-button fill="clear" (click)="sendMessage()">
+        <ion-icon name="send"></ion-icon>
+      </ion-button>
+    </div>
+  </div>
+
+  紧急求助浮窗
+  <div class="emergency-card" [class.active]="showEmergency">
+    <div class="header">需要立即帮助吗?</div>
+    <div class="actions">
+      <ion-button expand="block" fill="solid" color="danger">
+        <ion-icon name="call"></ion-icon>
+        联系紧急联系人
+      </ion-button>
+      <ion-button expand="block" fill="outline" (click)="startBreathingGuide()">
+        <ion-icon name="play-circle"></ion-icon>
+        启动呼吸引导
+      </ion-button>
+    </div>
+  </div>
+</ion-content> -->

+ 148 - 0
MindOCApp/src/app/ai/ai.page.scss

@@ -0,0 +1,148 @@
+/* ai.page.scss */
+.input {
+  border-radius: 10px;
+  outline: 2px solid #FEBF00;
+  border: 0;
+  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+  background-color: #e2e2e2;
+  outline-offset: 3px;
+  padding: 10px 1rem;
+  transition: 0.25s;
+}
+
+.input:focus {
+  outline-offset: 5px;
+  background-color: #fff
+}
+.ai-guard {
+    --primary-color: #64C3D3;
+    --danger-color: #FF6B6B;
+    --text-color: #4A4A6A;
+  
+    .emotion-meter {
+      height: 200px;
+      background: linear-gradient(145deg, #F0F9FF, #E1F3FF);
+      position: relative;
+      overflow: hidden;
+  
+      .wave {
+        position: absolute;
+        bottom: 0;
+        width: 200%;
+        height: 100px;
+        background: url('data:image/svg+xml,...波浪SVG数据...');
+        animation: waveFlow 15s linear infinite;
+      }
+  
+      .face-avatar {
+        position: absolute;
+        left: 50%;
+        top: 30%;
+        transform: translateX(-50%);
+        width: 100px;
+        height: 100px;
+        border-radius: 50%;
+        overflow: hidden;
+        box-shadow: 0 8px 24px rgba(100,195,211,0.3);
+  
+        img {
+          width: 100%;
+          transition: filter 0.5s ease;
+        }
+  
+        &:hover img {
+          filter: hue-rotate(30deg);
+        }
+      }
+    }
+  
+    .chat-container {
+      margin-top: -40px;
+      background: white;
+      border-radius: 40px 40px 0 0;
+      padding: 20px;
+      min-height: calc(100vh - 160px);
+  
+      .messages {
+        max-height: 60vh;
+        overflow-y: auto;
+  
+        .message {
+          margin: 12px 0;
+          transition: transform 0.3s ease;
+  
+          &.ai {
+            .bubble {
+              background: #F0F9FF;
+              border-radius: 20px 20px 20px 4px;
+            }
+          }
+  
+          .bubble {
+            max-width: 80%;
+            padding: 12px 16px;
+            background: var(--primary-color);
+            color: white;
+            border-radius: 20px 20px 4px 20px;
+  
+            .meta {
+              display: flex;
+              align-items: center;
+              gap: 8px;
+              font-size: 12px;
+              margin-top: 8px;
+              opacity: 0.8;
+            }
+          }
+        }
+      }
+  
+      .input-area {
+        display: flex;
+        gap: 12px;
+        padding: 16px;
+        background: rgba(240,249,255,0.9);
+        border-radius: 30px;
+        margin-top: 20px;
+  
+        ion-textarea {
+          --padding-start: 12px;
+          background: white;
+          border-radius: 20px;
+        }
+      }
+    }
+  
+    .emergency-card {
+      position: fixed;
+      bottom: -200px;
+      left: 20px;
+      right: 20px;
+      background: white;
+      border-radius: 20px;
+      box-shadow: 0 8px 32px rgba(255,107,107,0.2);
+      transition: bottom 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
+  
+      &.active {
+        bottom: 20px;
+      }
+  
+      .header {
+        padding: 16px;
+        font-weight: 500;
+        color: var(--danger-color);
+        border-bottom: 1px solid #eee;
+      }
+  
+      .actions {
+        padding: 16px;
+        display: grid;
+        gap: 12px;
+      }
+    }
+  }
+  
+  @keyframes waveFlow {
+    0% { transform: translateX(0); }
+    100% { transform: translateX(-50%); }
+  }

+ 145 - 2
MindOCApp/src/app/ai/ai.page.ts

@@ -1,4 +1,16 @@
-import { Component } from '@angular/core';
+import { Component, ViewChild, ElementRef } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { AnimationController, IonContent } from '@ionic/angular';
+import { Haptics } from '@capacitor/haptics';
+
+// interface ChatMessage {
+//   content: string;
+//   isAI: boolean;
+//   time: Date;
+//   suggestion?: string;
+// }
+
+
 
 @Component({
   selector: 'app-ai',
@@ -8,6 +20,137 @@ import { Component } from '@angular/core';
 })
 export class AiPage {
 
-  constructor() {}
+  // @ViewChild(IonContent) contentArea!: IonContent;
+  // @ViewChild('messageContainer') msgContainer!: ElementRef;
+  
+  // // 对话数据
+  // chatHistory: ChatMessage[] = [
+  //   { 
+  //     content: '你好,我是你的心灵守护者小安,随时愿意倾听你的感受~', 
+  //     isAI: true, 
+  //     time: new Date() 
+  //   }
+  // ];
+  
+  // userInput = '';
+  // showEmergency = false;
+  // currentMood = {
+  //   level: 7.2,
+  //   avatar: 'assets/ai-avatar/normal.webp'
+  // };  
+
+  constructor(
+    //private http: HttpClient,
+    //private animationCtrl: AnimationController
+  ) {}
+
+  // // 发送消息
+  // async sendMessage() {
+  //   if (!this.userInput.trim()) return;
+
+  //   // 用户消息
+  //   this.addUserMessage(this.userInput);
+    
+  //   // AI回复
+  //   const aiResponse = await this.generateAIResponse(this.userInput);
+  //   this.addAIMessage(aiResponse);
+
+  //   // 滚动到底部
+  //   this.scrollToBottom();
+  //   this.userInput = '';
+  // }
+  
+  // private async scrollToBottom() {  
+  //   if (this.contentArea) {  
+  //     await this.contentArea.scrollToBottom(300);  // 300ms 滚动动画  
+  //   }  
+  // }  
+
+  // // 生成AI回复
+  // private async generateAIResponse(text: string): Promise<string> {
+  //   try {
+  //     // 调用本地LLM接口
+  //     const response = await this.http.post<{ reply?: string }>('/api/ai/chat', {
+  //       message: text,
+  //       mood: this.currentMood.level
+  //     }).toPromise();
+  //     // 安全访问响应数据
+  //     const reply = response?.reply ?? '我现在好像有点困惑,可以再说一次吗?';
+
+  //     // 触发干预检测
+  //     this.checkIntervention(reply);
+      
+  //     return reply;
+  //   } catch (error) {
+  //     console.error('AI回复失败:', error);
+  //     return '我现在好像有点困惑,可以再说一次吗?';
+  //   }
+  // }
+
+
+  
+  // // 添加用户消息
+  // private addUserMessage(text: string) {
+  //   this.chatHistory.push({
+  //     content: text,
+  //     isAI: false,
+  //     time: new Date()
+  //   });
+  //   this.playMessageAnimation();
+  // }
+
+  // // 添加AI消息
+  // private addAIMessage(text: string) {
+  //   this.chatHistory.push({
+  //     content: text,
+  //     isAI: true,
+  //     time: new Date(),
+  //     suggestion: this.getSuggestionTag(text)
+  //   });
+  //   this.playMessageAnimation();
+  // }
+
+  // // 新增建议标签生成
+  // private getSuggestionTag(text: string): string {
+  //   const suggestions = [
+  //     { keywords: ['呼吸', '放松'], tag: '呼吸练习' },
+  //     { keywords: ['运动', '活动'], tag: '身体调节' },
+  //     { keywords: ['记录', '写下'], tag: '情绪日记' },
+  //     { keywords: ['冥想', '正念'], tag: '正念练习' }
+  //   ];
+
+  //   return suggestions.find(s => 
+  //     s.keywords.some(kw => text.includes(kw))
+  //   )?.tag || '';
+  // }
+
+  // // 消息入场动画
+  // private playMessageAnimation() {
+  //   const bubbles = this.msgContainer.nativeElement.querySelectorAll('.message');
+  //   const lastBubble = bubbles[bubbles.length - 1];
+    
+  //   this.animationCtrl.create()
+  //     .addElement(lastBubble)
+  //     .duration(500)
+  //     .easing('cubic-bezier(0.4, 0, 0.2, 1)')
+  //     .beforeStyles({
+  //       opacity: 0,
+  //       transform: 'translateY(20px)'
+  //     })      
+  //     .fromTo('transform', 'translateY(20px)', 'translateY(0)')
+  //     .fromTo('opacity', '0', '1')
+  //     .play();
+  // }
+
+  // // 紧急状态检测
+  // private checkIntervention(response: string) {
+  //   const dangerKeywords = ['自杀', '自伤', '极端'];
+  //   const isEmergency = dangerKeywords.some(kw => response.includes(kw));
+    
+  //   if (isEmergency) {
+  //     this.showEmergency = true;
+  //     Haptics.vibrate({ duration: 1000 });
+  //   }
+  // }
 
 }

+ 51 - 1
MindOCApp/src/app/game/game.page.html

@@ -3,7 +3,9 @@
   <div class="game-mode-switch">
     <ion-segment [(ngModel)]="currentGame" (ionChange)="switchGameMode()">
       <ion-segment-button value="bubble">
-        <ion-label style="font-size: 16px;">捏泡泡</ion-label>
+        <ion-label style="font-size: 16px;">
+          捏泡泡
+        </ion-label>
       </ion-segment-button>
       <ion-segment-button value="sand">
         <ion-label style="font-size: 16px;">禅意沙画</ion-label>
@@ -11,6 +13,54 @@
     </ion-segment>
   </div>
 
+  <div class="card">
+    <p class="heading">
+      Popular this month
+    </p>
+    <p>
+      Powered By
+    </p>
+    <p>
+      Uiverse
+    </p>
+  </div>
+
+  <div>
+    <ion-segment [(ngModel)]="currentGame" (ionChange)="switchGameMode()">
+      <ion-segment-button value="bubble">
+        <ion-label>
+          <div class="card">
+            <p class="heading">
+              Popular this month
+            </p>
+            <p>
+              Powered By
+            </p>
+            <p>
+              捏泡泡
+            </p>
+          </div>
+        </ion-label>
+      </ion-segment-button>
+      <ion-segment-button value="sand">
+        <ion-label>
+          <div class="card">
+            <p class="heading">
+              Popular this month
+            </p>
+            <p>
+              Powered By
+            </p>
+            <p>
+              禅意沙画
+            </p>
+          </div>
+        </ion-label>
+      </ion-segment-button>
+    </ion-segment>
+  </div>
+
+
   <!-- 游戏画布容器 -->
   <div #gameCanvasContainer class="canvas-container">
     <canvas #gameCanvas></canvas>

+ 64 - 0
MindOCApp/src/app/game/game.page.scss

@@ -1,4 +1,68 @@
 /* game.page.scss */
+/* card*/
+.card {
+  position: relative;
+  width: 190px;
+  height: 254px;
+  background-color: #000;
+  display: flex;
+  flex-direction: column;
+  justify-content: end;
+  padding: 12px;
+  gap: 12px;
+  border-radius: 8px;
+  cursor: pointer;
+}
+
+.card::before {
+  content: '';
+  position: absolute;
+  inset: 0;
+  left: -5px;
+  margin: auto;
+  width: 200px;
+  height: 264px;
+  border-radius: 10px;
+  background: linear-gradient(-45deg, #e81cff 0%, #40c9ff 100% );
+  z-index: -10;
+  pointer-events: none;
+  transition: all 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275);
+}
+
+.card::after {
+  content: "";
+  z-index: -1;
+  position: absolute;
+  inset: 0;
+  background: linear-gradient(-45deg, #fc00ff 0%, #00dbde 100% );
+  transform: translate3d(0, 0, 0) scale(0.95);
+  filter: blur(20px);
+}
+
+.heading {
+  font-size: 20px;
+  text-transform: capitalize;
+  font-weight: 700;
+}
+
+.card p:not(.heading) {
+  font-size: 14px;
+}
+
+.card p:last-child {
+  color: #e81cff;
+  font-weight: 600;
+}
+
+.card:hover::after {
+  filter: blur(30px);
+}
+
+.card:hover::before {
+  transform: rotate(-90deg) scaleX(1.34) scaleY(0.77);
+}
+
+
 .game-container {
     --game-primary: #FF9A9E;
     --game-secondary: #A8EDEA;

二进制
MindOCApp/src/assets/ai-avatar/normal.webp