15270821319 преди 6 месеца
родител
ревизия
4c9e783291
променени са 3 файла, в които са добавени 139 реда и са изтрити 99 реда
  1. 31 8
      AiStudy-app/src/app/tab1/tab1.page.html
  2. 37 37
      AiStudy-app/src/app/tab1/tab1.page.scss
  3. 71 54
      AiStudy-app/src/app/tab1/tab1.page.ts

+ 31 - 8
AiStudy-app/src/app/tab1/tab1.page.html

@@ -1,30 +1,53 @@
 <ion-header [translucent]="true">
   <ion-toolbar>
-    <ion-title>对话</ion-title>
+    <ion-title>AI学习交互</ion-title>
   </ion-toolbar>
 </ion-header>
 
 <ion-content [fullscreen]="true" class="ion-padding">
-  <!-- 聊天消息区域 -->
   <div class="chat-container">
-    <div *ngFor="let message of messages" [ngClass]="{'user-message': message.isUser, 'ai-message': !message.isUser}" class="message-wrapper">
+    <!-- 历史消息显示 -->
+    <div *ngFor="let message of messages" 
+         [ngClass]="{'user-message': message.isUser, 'ai-message': !message.isUser}" 
+         class="message-wrapper">
       <div class="message-bubble">
-        {{ message.content }}
+        <!-- 使用 markdown 预览组件显示 AI 回复 -->
+        @if(message.isUser) {
+          <div class="message-content">{{ message.content }}</div>
+        } @else {
+          <fm-markdown-preview class="message-content" [content]="message.content"></fm-markdown-preview>
+        }
+        <div class="message-time">
+          {{ message.timestamp | date:'HH:mm' }}
+        </div>
       </div>
     </div>
+    
+    <!-- 实时消息预览 -->
+    @if(isLoading && !isComplete) {
+      <div class="ai-message message-wrapper">
+        <div class="message-bubble">
+          <div class="message-content">{{ currentResponse }}</div>
+        </div>
+      </div>
+    }
   </div>
 </ion-content>
 
-<!-- 底部输入区域 -->
 <ion-footer>
   <ion-toolbar>
     <ion-item>
-      <ion-input 
+      <ion-textarea
         [(ngModel)]="userInput" 
         placeholder="请输入消息..." 
         (keyup.enter)="sendMessage()"
-      ></ion-input>
-      <ion-button slot="end" (click)="sendMessage()" [disabled]="!userInput">
+        [disabled]="isLoading"
+        autoGrow="true"
+        rows="1"
+      ></ion-textarea>
+      <ion-button slot="end" 
+                  (click)="sendMessage()" 
+                  [disabled]="!userInput || isLoading">
         <ion-icon name="send"></ion-icon>
       </ion-button>
     </ion-item>

+ 37 - 37
AiStudy-app/src/app/tab1/tab1.page.scss

@@ -10,7 +10,7 @@
 
 .message-wrapper {
   display: flex;
-  margin: 5px 0;
+  margin: 8px 0;
   
   &.user-message {
     justify-content: flex-end;
@@ -22,61 +22,61 @@
 }
 
 .message-bubble {
-  max-width: 70%;
-  padding: 10px 15px;
-  border-radius: 15px;
+  max-width: 75%;
+  padding: 12px 16px;
+  border-radius: 18px;
   word-wrap: break-word;
+  position: relative;
 }
 
 .user-message .message-bubble {
-  background-color: #007aff;
+  background-color: var(--ion-color-primary);
   color: white;
+  margin-left: 20%;
 }
 
 .ai-message .message-bubble {
-  background-color: #e5e5ea;
-  color: black;
+  background-color: var(--ion-color-light);
+  color: var(--ion-color-dark);
+  margin-right: 20%;
 }
 
-ion-split-pane {
-  --side-width: 300px;
-  --side-max-width: 300px;
+.message-content {
+  font-size: 16px;
+  line-height: 1.4;
 }
 
-ion-menu {
-  --width: 300px;
-  --max-width: 300px;
+.message-time {
+  font-size: 12px;
+  opacity: 0.7;
+  margin-top: 4px;
+  text-align: right;
 }
 
-ion-item.selected {
-  --background: var(--ion-color-light);
+.loading-indicator {
+  display: flex;
+  justify-content: center;
+  padding: 20px;
+}
+
+ion-footer ion-toolbar {
+  --padding-top: 0;
+  --padding-bottom: 0;
 }
 
 ion-item {
-  --padding-start: 16px;
-  --padding-end: 16px;
+  --padding-start: 8px;
+  --padding-end: 8px;
+  --inner-padding-end: 8px;
+}
+
+ion-input {
+  --padding-start: 12px;
+  --padding-end: 12px;
   --padding-top: 8px;
   --padding-bottom: 8px;
-  
-  h3 {
-    font-size: 16px;
-    font-weight: 500;
-    margin-bottom: 4px;
-  }
-  
-  p {
-    color: var(--ion-color-medium);
-    font-size: 14px;
-    margin: 0;
-  }
-  
-  small {
-    color: var(--ion-color-medium);
-    font-size: 12px;
-  }
 }
 
-// 确保消息不会被输入框遮挡
-ion-content {
-  --padding-bottom: 70px;
+ion-button {
+  margin: 0;
 }

+ 71 - 54
AiStudy-app/src/app/tab1/tab1.page.ts

@@ -1,21 +1,16 @@
-import { Component } from '@angular/core';
-import { IonHeader, IonToolbar, IonTitle, IonContent, IonFooter, IonItem, IonInput, IonButton, IonIcon, IonSpinner } from '@ionic/angular/standalone';
+import { Component, ViewChild } from '@angular/core';
+import { IonHeader, IonToolbar, IonTitle, IonContent, IonFooter, IonItem, IonTextarea, IonButton, IonIcon } from '@ionic/angular/standalone';
 import { FormsModule } from '@angular/forms';
-import { NgClass, NgFor, NgIf } from '@angular/common';
+import { NgClass, NgFor, NgIf, DatePipe } from '@angular/common';
 import { addIcons } from 'ionicons';
 import { send } from 'ionicons/icons';
-import { HttpClient, HttpHeaders } from '@angular/common/http';
-import Parse from 'parse';
-
-// 定义API响应类型
-interface ChatResponse {
-  reply: string;
-  [key: string]: any;
-}
+import { FmodeChatCompletion, MarkdownPreviewModule } from 'fmode-ng';
 
+// 定义消息接口
 interface ChatMessage {
   content: string;
   isUser: boolean;
+  timestamp: Date;
 }
 
 @Component({
@@ -30,74 +25,96 @@ interface ChatMessage {
     IonContent, 
     IonFooter,
     IonItem,
-    IonInput,
+    IonTextarea,
     IonButton,
     IonIcon,
-    IonSpinner,
     FormsModule,
     NgClass,
     NgFor,
-    NgIf
+    NgIf,
+    DatePipe,
+    MarkdownPreviewModule
   ],
 })
 export class Tab1Page {
+  @ViewChild(IonContent) content!: IonContent;
+  
   messages: ChatMessage[] = [];
   userInput: string = '';
   isLoading: boolean = false;
+  isComplete: boolean = false;
+  currentResponse: string = '';
 
-  constructor(private http: HttpClient) {
+  constructor() {
     addIcons({ send });
   }
 
+  // 发送消息
   async sendMessage() {
     if (this.userInput.trim() && !this.isLoading) {
       this.isLoading = true;
+      this.isComplete = false;
+      this.currentResponse = '';
 
       // 添加用户消息
-      this.messages.push({
+      const userMessage: ChatMessage = {
         content: this.userInput,
-        isUser: true
-      });
-
-      // 准备AI回复占位
-      this.messages.push({
-        content: '',
-        isUser: false
-      });
-
-      try {
-        // 使用直接的HTTP请求
-        const headers = new HttpHeaders({
-          'X-Parse-Application-Id': 'ncloudmaster',
-          'X-Parse-Session-Token': Parse.User.current()?.getSessionToken() || '',
-          'Content-Type': 'application/json'
-        });
+        isUser: true,
+        timestamp: new Date()
+      };
+      this.messages.push(userMessage);
 
-        const response = await this.http.post<ChatResponse>(
-          'https://server.fmode.cn/api/apig/chat',
-          { message: this.userInput },
-          { headers }
-        ).toPromise();
+      // 创建 completion 实例
+      const completion = new FmodeChatCompletion([
+        { role: "system", content: "你是一个专业的教师,可以帮助用户解答各种学习问题。" },
+        { role: "user", content: this.userInput }
+      ]);
 
-        console.log('API Response:', response);
-
-        // 更新AI回复
-        if (this.messages.length > 0 && response) {
-          const aiMessage = this.messages[this.messages.length - 1];
-          aiMessage.content = response.reply || '抱歉,我没有收到有效的回复。';
+      // 发送请求并订阅响应
+      completion.sendCompletion().subscribe({
+        next: (message: any) => {
+          this.currentResponse = message.content;
+          
+          if (message?.complete) {
+            this.isComplete = true;
+            this.isLoading = false;
+            
+            // 添加 AI 回复到消息列表
+            const aiMessage: ChatMessage = {
+              content: this.currentResponse,
+              isUser: false,
+              timestamp: new Date()
+            };
+            this.messages.push(aiMessage);
+            
+            // 清空输入和当前响应
+            this.userInput = '';
+            this.currentResponse = '';
+            
+            // 滚动到底部
+            this.scrollToBottom();
+          }
+        },
+        error: (error) => {
+          console.error('发送消息失败:', error);
+          this.isLoading = false;
+          this.messages.push({
+            content: '抱歉,发生了一些错误,请稍后再试。',
+            isUser: false,
+            timestamp: new Date()
+          });
+          this.scrollToBottom();
         }
+      });
+    }
+  }
 
-      } catch (error) {
-        console.error('Chat Error:', error);
-        
-        // 显示错误消息
-        if (this.messages.length > 0) {
-          this.messages[this.messages.length - 1].content = '抱歉,发生了一些错误,请稍后再试。';
-        }
-      } finally {
-        this.isLoading = false;
-        this.userInput = ''; // 清空输入框
-      }
+  // 滚动到底部
+  async scrollToBottom() {
+    try {
+      await this.content.scrollToBottom(300);
+    } catch (error) {
+      console.error('滚动失败:', error);
     }
   }
 }