焦怡璇 1 年之前
父节点
当前提交
a1654ba9d7

+ 9 - 3
heartvoice-app/src/app/report/report.component.html

@@ -1,3 +1,9 @@
-<p>
-  report works!
-</p>
+<ion-header>
+  <ion-toolbar>
+    <ion-title>您的报告</ion-title>
+  </ion-toolbar>
+</ion-header>
+<img src="assets/img/9.jpg" alt="  " width="100%" height="200"/> 
+<ion-content>
+<div>{{responseMsg}}</div>
+</ion-content>

+ 37 - 0
heartvoice-app/src/app/report/report.component.scss

@@ -0,0 +1,37 @@
+
+  ion-toolbar {
+    --background: #f8d7da; /* 顶部工具栏背景色 */
+  }
+  h1 {
+    font-size: 26px; /* 标题字体大小 */
+    color: #333; /* 标题颜色 */
+    margin: 16px 0; /* 标题的上下外边距 */
+    text-align: center; /* 标题居中 */
+  } 
+  
+  ion-input, ion-textarea {
+    width: 100%; /* 输入框宽度 */
+    margin: 8px 0; /* 输入框的上下外边距 */
+    border-radius: 8px; /* 圆角效果 */
+    background-color: #edf0b2; /* 输入框背景色 */
+    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); /* 输入框阴影效果 */
+    padding: 10px; /* 输入框内边距 */
+    font-size: 16px; /* 字体大小 */
+  }
+  
+  ion-textarea {
+    min-height: 100px; /* 设置文本域的最小高度 */
+  }
+  
+
+  
+  div {
+    margin-top: 20px; /* 消息展示的顶部外边距 */
+    padding: 12px; /* 消息展示的内边距 */
+    background-color: #f8d7da; /* 消息展示背景色 */
+    border-radius: 8px; /* 消息展示圆角 */
+    width: 100%; /* 消息展示宽度 */
+    text-align: center; /* 文本居中 */
+    font-size: 16px; /* 字体大小 */
+    color: #444; /* 字体颜色 */
+  }

+ 143 - 30
heartvoice-app/src/app/report/report.component.ts

@@ -1,24 +1,30 @@
 import { Component, OnInit } from '@angular/core';
 import { ActivatedRoute } from '@angular/router';
-import { Cloudmy } from 'src/lib/ncloud';
+import { Cloudmy,Cloudget } from 'src/lib/ncloud';
 import { CloudUser } from 'src/lib/ncloud';
-import Sentiment from 'sentiment'; // 导入 Sentiment.js
+import Sentiment from 'sentiment'; // 导入 Sentiment.js 用于情感分析
 import axios from 'axios'; // 导入 axios 进行 HTTP 请求
-import md5 from 'md5'; // 导入 md5 库
+import md5 from 'md5'; // 导入 md5 库用于生成签名
+import { FmodeChatCompletion } from 'fmode-ng'; // 导入 FmodeChatCompletion 以进行聊天完成请求
+import { IonicModule } from '@ionic/angular';
 
 @Component({
   selector: 'app-report',
   templateUrl: './report.component.html',
   styleUrls: ['./report.component.scss'],
   standalone: true,
+  imports: [IonicModule],
 })
 export class ReportComponent implements OnInit {
   data: any; // 用于存储接收到的数据
   extractedContents: string[] = []; // 用于存储提取的内容
   translatedText: string = ''; // 用于存储翻译后的文本
-  data1: any; // 用于存储最终的数据
-  data2: any;
-  data3: any;
+  data1: any; // 用于存储 MBTI 类型
+  data2: any; // 用于存储情感得分
+  data3: any; // 用于存储合并后的内容
+  data4: any;
+  data5: any;
+  responseMsg: string = ""; // 用于展示消息内容的变量
 
   constructor(private route: ActivatedRoute) {}
 
@@ -29,18 +35,30 @@ export class ReportComponent implements OnInit {
         try {
           this.data = JSON.parse(params['data']); // 尝试解析数据
           console.log('成功返回数据:', this.data); // 打印成功消息和接收到的数据
-
+  
+          const query = new Cloudget('psychotherapy');
+          query.descending('createdAt'); // 按照 createdAt 降序排列
+          const latestResponse = await query.first(); // 获取最近一次的记录
+  
+          // 如果找到记录,赋值给 data5
+          if (latestResponse && latestResponse.content) {
+            this.data5 = latestResponse.content;
+            console.log('data5:', this.data5);
+          } else {
+            console.log('没有找到心理疏通文件');
+          }
+  
           // 检查第一个数据是否为 'yes'
           if (this.data.data.data1 === 'yes') {
             // 如果是 'yes',查询 Userresponse 表获取 mbtiType
             const currentUser = new CloudUser();
             const userId = currentUser.toPointer(); // 获取当前用户 ID
-
+  
             const query = new Cloudmy('Userresponse');
             query.equalTo('user', userId);
             query.descending('createdAt'); // 按照 createdAt 降序排列
             const latestResponse = await query.first(); // 获取最近一次的记录
-
+  
             // 如果找到记录,赋值给 data1
             if (latestResponse && latestResponse.mbtiType) {
               this.data1 = latestResponse.mbtiType;
@@ -53,6 +71,30 @@ export class ReportComponent implements OnInit {
             this.data1 = this.data.data.data1;
             console.log('赋值给 data1 的MBTI 类型:', this.data1);
           }
+  
+          // 检查第二个数据是否为 'yes'
+          if (this.data.data.data2 === 'yes') {
+            let currentUser = new CloudUser();
+            let userId = currentUser.toPointer(); // 替换为实际的用户ID
+  
+            const query = new Cloudmy('Chat');
+            query.equalTo('user', userId);
+            query.descending('chattime'); // 按照 chattime 降序排列
+            const latestChat = await query.first(); // 获取最新聊天记录
+  
+            if (latestChat && latestChat.content) {
+              this.extractOddIndexedContents(latestChat.content);
+            }
+          } else {
+            // 如果不是 'yes'
+            this.data2 = "没数据"; 
+            this.data3 = "没数据"
+            this.data4 = "没数据"// 确保 data1 被赋予一个空字符串
+            console.log('否');
+          }
+  
+          // 等待数据处理完成后再发送消息
+          await this.waitForData();
         } catch (error) {
           console.error('解析数据时发生错误:', error); // 捕获解析错误
         }
@@ -61,44 +103,113 @@ export class ReportComponent implements OnInit {
         console.log('没有接收到数据。'); // 打印没有接收到数据的消息
       }
     });
+  }
+  
+  // 等待数据处理完成的方法
+  private async waitForData() {
+    while (this.data2 === undefined || this.data3 === undefined) {
+      await new Promise(resolve => setTimeout(resolve, 100)); // 暂停100ms再检查
+    }
+    
+    // 发送消息
+    this.sendMessage();
+  }
+  
+  // 发送消息的方法
+  sendMessage() {
+    console.log("创建消息");
+    console.log("data1:", this.data1);
+    console.log("data2:", this.data2);
+    console.log("data3:", this.data3);
+    console.log("data4:", this.data4);
+    console.log("data5:", this.data5);
+  
+    // 检查所有需要的值是否已赋值
+    if (this.data1 && this.data2 !== undefined && this.data3 && this.data4 && this.data5) {
+      let PromptTemplate = `
+      你是一名专业的心理疏导方向的专家,
+      用户的ti性格是${this.data1},
+      用户当前的情感得分为${this.data2},
+      情感分析结果是${this.data3},
+      用户与AI的中用户聊天内容是${this.data4},
+      请你根据他的性格,情感得分,情感分析结果以及用户的聊天内容,导出一份独属于用户的个人心理状态评估研究报告。
+      以下是模板{
+个人心理状态评估研究报告
 
-    // 检查第二个数据是否为 'yes'
-    if ( this.data.data.data2 === 'yes') {
-      console.log('否');
-      let currentUser = new CloudUser();
-      let userId = currentUser.toPointer(); // 替换为实际的用户ID
+一、评估方法
 
-      const query = new Cloudmy('Chat');
-      query.equalTo('user', userId);
-      query.descending('chattime'); // 按照chattime降序排列
-      const latestChat = await query.first(); // 获取最新聊天记录
+性格测试:通过进行性格测试,了解您的大致性格,****
 
-      if (latestChat && latestChat.content) {
-        this.extractOddIndexedContents(latestChat.content);
-      }
+聊天对话:(如果聊天内容没数据就显示无数据)
+(有数据的话显示
+通过你与AI的对话对您的情绪进行分析,察觉到您的情绪不太好,(不要显示用户表达的话,和情感得分))***
+
+二、评估结果
+
+性格偏向:你是一个***人,***
+
+情绪状态:*****
+
+认知与情绪调节:****
+
+三、建议
+
+1.增强自我认知***
+
+2.专业咨询***
+
+3.情绪调节训练****
+
+}
+      如果用户当前的情感得分为${this.data2}<0,参考${this.data5},再推荐1~2个心理治疗方法给用户
+      {
+        四、推荐心理治疗方法
+
+        1.***
+      
+      2.****}。
+      不要显示用户基本信息`
+      ;
+  
+      let completion = new FmodeChatCompletion([
+        { role: "user", content: PromptTemplate }
+      ]);
+  
+      completion.sendCompletion().subscribe((message: any) => {
+        // 打印消息体
+        console.log(message.content);
+        // 赋值消息内容给组件内属性
+        this.responseMsg = message.content;
+      });
     } else {
-      // 如果不是 'yes'
-      this.data1 = ""; // 确保 data1 被赋予一个空字符串
-      console.log('否');
+      console.warn("发送消息失败: 数据未完全赋值", {
+        data1: this.data1,
+        data2: this.data2,
+        data3: this.data3,
+        data4: this.data4,
+        data5: this.data5
+      });
     }
   }
+  
 
-    private extractOddIndexedContents(contentArray: any[]) {
+  // 提取聊天记录中奇数索引的内容
+  private extractOddIndexedContents(contentArray: any[]) {
     this.extractedContents = contentArray
       .filter((_, index) => index % 2 === 0) // 过滤出奇数索引的对象
       .slice(1) // 去掉第一个对象
-      .map(item => item.content); // 提取content的值
+      .map(item => item.content); // 提取 content 的值
 
-    // 明确指定 combinedContent 的类型为 string
+    // 合并内容
     const combinedContent: string = this.extractedContents.join(' ');
-
-    // 打印合并后的字符串
     console.log('合并后的内容:', combinedContent);
+    this.data4 = combinedContent;
 
     // 调用翻译函数
     this.translateContent(combinedContent);
   }
 
+  // 翻译内容
   private async translateContent(content: string) {
     const appid = '20241224002237024'; // 替换为您的百度翻译API的appid
     const key = 'sNvEvziEi9iu8QxbjjoV'; // 替换为您的百度翻译API的key
@@ -131,6 +242,7 @@ export class ReportComponent implements OnInit {
     }
   }
 
+  // 情感分析
   private analyzeSentiment(text: string) {
     console.log('进行情感分析的文本:', text); 
     const sentiment = new Sentiment();
@@ -142,8 +254,9 @@ export class ReportComponent implements OnInit {
     this.data2 = result.score;
     this.data3 = result;
   }
-
 }
+    
+
 
   //   this.translatedText = this.translateContent(combinedContent); // 翻译合并后的内容
   //   console.log('翻译结果:', this.translatedText);

+ 6 - 0
heartvoice-app/src/app/tab1/tab1.page.html

@@ -83,6 +83,12 @@
             <p>我们承诺保护用户隐私,所有对话内容均为保密。</p>
           </ion-label>
         </ion-item>
+        <ion-item>
+          <ion-label>
+            <h2>历史记录在哪看?</h2>
+            <p>点击我的页面,查看历史记录。</p>
+          </ion-label>
+        </ion-item>
       </ion-list>
     </ion-card-content>
   </ion-card>

+ 110 - 102
heartvoice-app/src/app/tab1/tab1.page.scss

@@ -1,115 +1,123 @@
 /* 首页样式 */
 
 ion-header {
-    background-color: #f8d7da; /* 顶部背景色 */
-    color: white; /* 文字颜色 */
-  }
-  
-  ion-title{
-    font-family: "宋体";
-    font-weight: bold;
-    font: size 20px;
-    text-align: center;
-  }
-  ion-toolbar {
-    --background: #f8d7da; /* 顶部工具栏背景色 */
-  }
-  
+  background-color: #f8d7da; /* 顶部背景色 */
+  color: white; /* 文字颜色 */
+}
+
+ion-title{
+  font-family: "宋体";
+  font-weight: bold;
+  font: size 20px;
+  text-align: center;
+}
+ion-toolbar {
+  --background: #f8d7da; /* 顶部工具栏背景色 */
+}
+
+h2 {
+  color: #333; /* 主标题颜色 */
+  font-size: 1.5rem; /* 主标题字体大小 */
+  margin: 16px 0; /* 主标题上下间距 */
+  text-align: center; /* 主标题居中 */
+}
+
+p {
+  color: #666; /* 副标题颜色 */
+  font-size: 1rem; /* 副标题字体大小 */
+  margin-bottom: 16px; /* 副标题下方间距 */
+  text-align: center; /* 副标题居中 */
+}
+
+ion-carousel-slide {
+  display: flex;
+  flex-direction: column;
+  align-items: center; /* 居中对齐内容 */
+  justify-content: center; /* 垂直居中 */
+  padding: 20px; /* 幻灯片内边距 */
+}
+
+ion-card {
+  margin: 10px; /* 卡片之间的间距 */
+  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); /* 卡片阴影 */
+  border-radius: 8px; /* 卡片圆角 */
+}
+
+
+ion-card-header {
+  background-color: rgba(255, 221, 51, 0.1); /* 卡片头部背景色 */
+  padding: 10px; /* 头部内边距 */
+  border-top-left-radius: 8px; /* 圆角 */
+  border-top-right-radius: 8px; /* 圆角 */
+}
+
+ion-card-title {
+  font-size: 1.8rem; /* 卡片标题字体大小 */
+  text-align: center; /* 标题居中 */
+}
+
+ion-list {
+  margin: 0; /* 列表外边距 */
+}
+
+ion-item {
+  --background: transparent; /* 列表项背景透明 */
+}
+
+ion-avatar {
+  width: 50px; /* 头像宽度 */
+  height: 50px; /* 头像高度 */
+  border-radius: 50%; /* 头像圆形 */
+}
+
+ion-footer {
+  background-color: #f8d7da; /* 底部背景色 */
+  color: white; /* 底部文字颜色 */
+}
+
+ion-footer ion-title {
+  font-size: 0.9rem; /* 底部标题字体大小 */
+  text-align: center;
+}
+
+/* 提交反馈按钮的样式 */
+ion-button {
+--background: #f8d7da; /* 按钮背景色 */
+--color: #161616; /* 点击时按钮字体颜色 */
+}
+ion-button:hover {
+--background: rgba(255, 221, 51, 0.1); /* 悬停时按钮背景色 */
+}
+ion-button:active {
+--background: #f8d7da; /* 点击时按钮背景色 */
+--color: #f96372; /* 点击时按钮字体颜色 */
+}
+
+/* 响应式设计 */
+@media (max-width: 576px) {
   h2 {
-    color: #333; /* 主标题颜色 */
-    font-size: 1.5rem; /* 主标题字体大小 */
-    margin: 16px 0; /* 主标题上下间距 */
-    text-align: center; /* 主标题居中 */
+    font-size: 1.2rem; /* 小屏幕主标题字体大小 */
   }
-  
+
   p {
-    color: #666; /* 副标题颜色 */
-    font-size: 1rem; /* 副标题字体大小 */
-    margin-bottom: 16px; /* 副标题下方间距 */
-    text-align: center; /* 副标题居中 */
-  }
-  
-  ion-carousel-slide {
-    display: flex;
-    flex-direction: column;
-    align-items: center; /* 居中对齐内容 */
-    justify-content: center; /* 垂直居中 */
-    padding: 20px; /* 幻灯片内边距 */
-  }
-  
-  ion-card {
-    margin: 10px; /* 卡片之间的间距 */
-    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); /* 卡片阴影 */
-    border-radius: 8px; /* 卡片圆角 */
-  }
-  
-  ion-card-header {
-    background-color: rgba(255, 221, 51, 0.1); /* 卡片头部背景色 */
+    font-size: 0.9rem; /* 小屏幕副标题字体大小 */
   }
-  
+
   ion-card-title {
-    font-size: 1.2rem; /* 卡片标题字体大小 */
-  }
-  
-  ion-list {
-    margin: 0; /* 列表外边距 */
-  }
-  
-  ion-item {
-    --background: transparent; /* 列表项背景透明 */
-  }
-  
-  ion-avatar {
-    width: 50px; /* 头像宽度 */
-    height: 50px; /* 头像高度 */
-    border-radius: 50%; /* 头像圆形 */
+    font-size: 1rem; /* 小屏幕卡片标题字体大小 */
   }
-  
-  ion-footer {
-    background-color: #f8d7da; /* 底部背景色 */
-    color: white; /* 底部文字颜色 */
-  }
-  
-  ion-footer ion-title {
-    font-size: 0.9rem; /* 底部标题字体大小 */
-    text-align: center;
-  }
-  
-/* 提交反馈按钮的样式 */
-ion-button {
-  --background: #f8d7da; /* 按钮背景色 */
-  --color: #161616; /* 点击时按钮字体颜色 */
 }
-ion-button:hover {
-  --background: rgba(255, 221, 51, 0.1); /* 悬停时按钮背景色 */
+
+
+app-edit-tag {
+  display: block; /* 使组件占据整个行 */
+  margin: 20px 0; /* 上下外边距 */
+  padding: 10px; /* 内边距 */
+  background-color: rgba(255, 221, 51, 0.1); /* 淡黄色背景 */
+  border: 1px solid #e6fb6e; /* 边框颜色 */
+  border-radius: 8px; /* 圆角边框 */
+  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); /* 阴影效果 */
 }
-ion-button:active {
-  --background: #f8d7da; /* 点击时按钮背景色 */
-  --color: #f96372; /* 点击时按钮字体颜色 */
-}
-  
-  /* 响应式设计 */
-  @media (max-width: 576px) {
-    h2 {
-      font-size: 1.2rem; /* 小屏幕主标题字体大小 */
-    }
-  
-    p {
-      font-size: 0.9rem; /* 小屏幕副标题字体大小 */
-    }
-  
-    ion-card-title {
-      font-size: 1rem; /* 小屏幕卡片标题字体大小 */
-    }
-  }
 
 
-  app-edit-tag {
-    display: block; /* 使组件占据整个行 */
-    margin: 20px 0; /* 上下外边距 */
-    padding: 10px; /* 内边距 */
-    background-color: rgba(255, 221, 51, 0.1); /* 淡黄色背景 */
-    border: 1px solid #e6fb6e; /* 边框颜色 */
-    border-radius: 8px; /* 圆角边框 */
-    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); /* 阴影效果 */
-  }
+

+ 6 - 2
heartvoice-app/src/app/tab2/tab2.page.html

@@ -3,7 +3,11 @@
     <ion-title>生成个性化心理报告</ion-title>
   </ion-toolbar>
 </ion-header>
-
+@if(!currentUser?.id) {
+  <p>请登入</p>
+}
+@if(currentUser?.id) {
 <ion-content>
   <ion-button expand="full" (click)="openModal()">生成报告</ion-button>
-</ion-content>
+</ion-content>
+}

+ 18 - 3
heartvoice-app/src/app/tab2/tab2.page.ts

@@ -2,6 +2,7 @@ import { Component } from '@angular/core';
 import { ModalHintComponent } from 'src/lib/user/modal-hint/modal-hint.component'; // 确保路径正确
 import { IonButton, IonContent, IonHeader, IonTitle, IonToolbar,ModalController } from '@ionic/angular/standalone';
 import {  Router } from '@angular/router';
+import { CloudUser } from 'src/lib/ncloud';
 @Component({
   selector: 'app-tab2',
   templateUrl: 'tab2.page.html',
@@ -10,9 +11,23 @@ import {  Router } from '@angular/router';
   imports: [IonHeader, IonToolbar, IonTitle, IonContent, IonButton], // 确保导入 IonicModule
 })
 export class Tab2Page {
-  constructor(private modalCtrl: ModalController,private router: Router) {}
+  handleRefresh(event:any) {
+    setTimeout(() => {
+      // Any calls to load data go here
+      this.currentUser = new CloudUser();
+      event.target.complete();
+    }, 2000);
+  }
+  currentUser:CloudUser|undefined
+  constructor(
+    private router: Router,
+    private modalCtrl:ModalController) {
+    this.currentUser = new CloudUser();
+  }
+
 
   async openModal() {
+
     const modal = await this.modalCtrl.create({
       component: ModalHintComponent,
       breakpoints: [0.7, 1.0],
@@ -30,11 +45,11 @@ export class Tab2Page {
     
 
    
+    
     this.router.navigate(['/tabs/report']);
-  
       const data1 = { data: data }; // 需要发送的数据
       this.router.navigate(['/tabs/report', { data: JSON.stringify(data1) }]); // 发送数据
-    
+   
 
   }
 }

+ 66 - 0
heartvoice-app/src/lib/ncloud.ts

@@ -460,4 +460,70 @@ export class Cloudmy {
         const json = await response?.json();
         return json?.results?.[0] || null;
     }
+}
+
+
+// Cloudget.ts
+
+export class Cloudget {
+    private className: string; // 数据表名称
+    private queryParams: Record<string, any> = {}; // 查询参数
+
+    constructor(className: string) {
+        this.className = className; // 设置数据表名称
+    }
+
+    // 设置等于条件
+    public equalTo(field: string, value: any) {
+        if (!this.queryParams["where"]) {
+            this.queryParams["where"] = {};
+        }
+        this.queryParams["where"][field] = value; // 添加条件
+        return this; // 支持链式调用
+    }
+
+    // 设置降序
+    public descending(field: string) {
+        if (!this.queryParams["order"]) {
+            this.queryParams["order"] = [];
+        }
+        this.queryParams["order"].push(`-${field}`); // 使用负号表示降序
+        return this; // 支持链式调用
+    }
+
+    // 执行查询并返回第一个结果
+    public async first(): Promise<any> {
+        let url = `https://dev.fmode.cn/parse/classes/${this.className}?`;
+
+        // 添加 where 条件
+        if (this.queryParams["where"] && Object.keys(this.queryParams["where"]).length) {
+            const whereStr = JSON.stringify(this.queryParams["where"]);
+            url += `where=${encodeURIComponent(whereStr)}`; // 使用 encodeURIComponent 进行编码
+        }
+
+        // 添加 order 条件
+        if (this.queryParams["order"] && this.queryParams["order"].length) {
+            const orderStr = this.queryParams["order"].join(',');
+            url += `&order=${orderStr}`;
+        }
+
+        // 发起请求
+        try {
+            const response = await fetch(url, {
+                headers: {
+                    "if-none-match": "W/\"1f0-ghxH2EwTk6Blz0g89ivf2adBDKY\"",
+                    "x-parse-application-id": "dev"
+                },
+                method: "GET",
+                mode: "cors",
+                credentials: "omit"
+            });
+
+            const json = await response.json();
+            return json?.results?.[0] || null; // 返回第一个结果或 null
+        } catch (error) {
+            console.error('查询时发生错误:', error);
+            throw error; // 抛出错误以便调用方处理
+        }
+    }
 }