焦怡璇 1 jaar geleden
bovenliggende
commit
8a644bca2f
31 gewijzigde bestanden met toevoegingen van 742 en 242 verwijderingen
  1. 3 3
      README.md
  2. 1 2
      heartvoice-app/src/app/app.routes.ts
  3. 19 3
      heartvoice-app/src/app/feedback/feedback.component.html
  4. 44 0
      heartvoice-app/src/app/feedback/feedback.component.scss
  5. 46 2
      heartvoice-app/src/app/feedback/feedback.component.ts
  6. 6 0
      heartvoice-app/src/app/rating-star/rating-star.component.html
  7. 11 0
      heartvoice-app/src/app/rating-star/rating-star.component.scss
  8. 22 0
      heartvoice-app/src/app/rating-star/rating-star.component.spec.ts
  9. 20 0
      heartvoice-app/src/app/rating-star/rating-star.component.ts
  10. 31 0
      heartvoice-app/src/app/record/record.component.html
  11. 72 0
      heartvoice-app/src/app/record/record.component.scss
  12. 22 0
      heartvoice-app/src/app/record/record.component.spec.ts
  13. 60 0
      heartvoice-app/src/app/record/record.component.ts
  14. 42 4
      heartvoice-app/src/app/report/report.component.ts
  15. 30 0
      heartvoice-app/src/app/review-display/review-display.component.html
  16. 58 0
      heartvoice-app/src/app/review-display/review-display.component.scss
  17. 22 0
      heartvoice-app/src/app/review-display/review-display.component.spec.ts
  18. 69 0
      heartvoice-app/src/app/review-display/review-display.component.ts
  19. 17 104
      heartvoice-app/src/app/review/review.component.html
  20. 39 82
      heartvoice-app/src/app/review/review.component.scss
  21. 53 11
      heartvoice-app/src/app/review/review.component.ts
  22. 4 4
      heartvoice-app/src/app/tab1/tab1.page.html
  23. 2 1
      heartvoice-app/src/app/tab1/tab1.page.scss
  24. 3 0
      heartvoice-app/src/app/tab1/tab1.page.ts
  25. 0 2
      heartvoice-app/src/app/tab2/tab2.page.html
  26. 2 2
      heartvoice-app/src/app/tab2/tab2.page.scss
  27. 1 0
      heartvoice-app/src/app/tab3/tab3.page.html
  28. 3 1
      heartvoice-app/src/app/tab3/tab3.page.scss
  29. 4 1
      heartvoice-app/src/app/tab3/tab3.page.ts
  30. 10 0
      heartvoice-app/src/app/tabs/tabs.routes.ts
  31. 26 20
      heartvoice-app/src/lib/ncloud.ts

+ 3 - 3
README.md

@@ -32,15 +32,15 @@ createdAt (创建时间)
 user (Pointer) (关联的用户)
 chatContent (聊天内容)
 chatTime (聊天时间)
-emotion (情绪状态)
+ (情绪指数)
+  (情绪   )
 
 PersonalityReport(个性化心理报告表)
 objectId (唯一标识)
 createdAt (创建时间)
 user (Pointer) (关联的用户)
-mbtiType (用户的MBTI类型)
 reportContent (报告内容)
-emotion (情绪状态)
+
 
 Feedback(用户反馈表)
 objectId (唯一标识)

+ 1 - 2
heartvoice-app/src/app/app.routes.ts

@@ -5,5 +5,4 @@ export const routes: Routes = [
     path: '',
     loadChildren: () => import('./tabs/tabs.routes').then((m) => m.routes),
   },
-];
-
+];

+ 19 - 3
heartvoice-app/src/app/feedback/feedback.component.html

@@ -1,3 +1,19 @@
-<p>
-  feedback 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 class="ion-padding">
+  <ion-item>
+    <ion-label position="floating">请输入您的反馈</ion-label>
+    <ion-input [(ngModel)]="userInput" type="text"></ion-input>
+  </ion-item>
+
+
+
+  <ion-button expand="full" (click)="submitFeedback()" class="ion-margin-top" routerLink="/chat">
+    <ion-icon slot="start" name="send"></ion-icon>
+    一键发表
+  </ion-button>
+</ion-content>

+ 44 - 0
heartvoice-app/src/app/feedback/feedback.component.scss

@@ -0,0 +1,44 @@
+// feedback.component.scss
+
+
+
+ion-toolbar {
+    --background: #f8d7da; /* 顶部工具栏背景色 */
+    font-family: "宋体";
+  }
+
+ion-content {
+    padding: 16px; /* 给内容添加内边距 */
+    --background: rgba(255, 221, 51, 0.1); /* 内容背景颜色 */
+
+    ion-item {
+        margin-bottom: 16px; /* 每个输入项之间的间距 */
+        background-color: #ffffff; /* 输入项背景颜色 */
+        border-radius: 8px; /* 圆角 */
+        box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* 阴影效果 */
+    }
+
+    ion-label {
+        color: #333; /* 标签颜色 */
+    }
+
+    ion-input {
+        font-size: 16px; /* 输入框字体大小 */
+        color: #666; /* 输入框字体颜色 */
+    }
+
+    ion-button {
+        margin-top: 16px; /* 按钮与输入框之间的间距 */
+        --background: #f8d7da; /* 按钮背景色 */
+        --color: rgb(9, 9, 9); /* 按钮文本颜色 */
+        border-radius: 5px; /* 按钮圆角 */
+
+        &:hover {
+            --background: rgba(255, 221, 51, 0.1); /* 悬停时按钮背景色 */
+        }
+
+        &:active {
+            --background: rgba(255, 221, 51, 0.1); /* 点击时按钮背景色 */
+        }
+    }
+}

+ 46 - 2
heartvoice-app/src/app/feedback/feedback.component.ts

@@ -1,15 +1,59 @@
+import { CommonModule } from '@angular/common';
 import { Component, OnInit } from '@angular/core';
-
+import { FormsModule } from '@angular/forms';
+import { IonicModule } from '@ionic/angular';
+import { Router } from '@angular/router';
+import { CloudUser,CloudObject } from 'src/lib/ncloud';
 @Component({
   selector: 'app-feedback',
   templateUrl: './feedback.component.html',
   styleUrls: ['./feedback.component.scss'],
   standalone: true,
+  imports: [IonicModule,CommonModule,FormsModule]
 })
 export class FeedbackComponent  implements OnInit {
+  userInput: string = '';
+submittedFeedback: string | null = null;
+
+submitFeedback() {
+  this.submittedFeedback = this.userInput;
+  console.log(this.userInput); // 打印输入框的数据
+  
+  let consult = new CloudObject("Feedback");
+  // 设置聊天记录的基本信息
+  let now = new Date();
+  let currentUser = new CloudUser();
+  let dateStr = `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()}`;
+  
+  // 对象权限的精确制定
+  let ACL: any = {
+    "*": { read: true, write: true }
+  };
+
+  consult.set({
+    user: currentUser.toPointer(),
+    feedbackContent: this.userInput, 
+    createdAt: dateStr, // 可以加上创建时间
+    // ACL: ACL
+  });
+
+  // 调用 save 方法来保存数据
+  consult.save().then(() => {
+    console.log('反馈保存成功');
+    this.userInput = ''; // 清空输入框
+    this.router.navigate(['/tabs/review-display']); // 跳转到显示页面
+  }).catch((error) => {
+    console.error('保存反馈时出错:', error);
+  });
+
+      this.userInput = ''; // 清空输入框
+  this.router.navigate(['/tabs/tab3']);
+}
+
 
-  constructor() { }
 
   ngOnInit() {}
 
+  constructor(private router: Router) {}
+
 }

+ 6 - 0
heartvoice-app/src/app/rating-star/rating-star.component.html

@@ -0,0 +1,6 @@
+<div class="star-rating">
+  <ion-icon *ngFor="let star of [].constructor(5); let i = index"
+            name="star"
+            [class.filled]="i < rating"
+            slot="start"></ion-icon>
+</div>

+ 11 - 0
heartvoice-app/src/app/rating-star/rating-star.component.scss

@@ -0,0 +1,11 @@
+
+  .star-rating {
+    ion-icon {
+        font-size: 30px; // 调整星星大小
+        margin-right: 5px; // 星星间距
+      
+      &.filled {
+        color: #ffd700; // 填充星星的颜色
+      }
+    }
+  }

+ 22 - 0
heartvoice-app/src/app/rating-star/rating-star.component.spec.ts

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

+ 20 - 0
heartvoice-app/src/app/rating-star/rating-star.component.ts

@@ -0,0 +1,20 @@
+import { CommonModule } from '@angular/common';
+import { Component, OnInit,Input } from '@angular/core';
+import { IonicModule } from '@ionic/angular';
+
+@Component({
+  selector: 'app-rating-star',
+  templateUrl: './rating-star.component.html',
+  styleUrls: ['./rating-star.component.scss'],
+  standalone: true,
+  imports: [IonicModule,CommonModule] // 将 IonicModule 添加到 imports
+})
+export class RatingStarComponent  implements OnInit {
+
+  constructor() { }
+
+  ngOnInit() {}
+  @Input() rating: number = 0; // 接收星级数
+}
+
+

+ 31 - 0
heartvoice-app/src/app/record/record.component.html

@@ -0,0 +1,31 @@
+<ion-header>
+  <ion-toolbar>
+    <ion-title>记录</ion-title>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content>
+  <div class="record-container" *ngIf="data || data1; else noDataTemplate">
+    <ion-card *ngIf="data">
+      <ion-card-header>
+        <ion-card-title>上一次测试的MBTI 类型</ion-card-title>
+      </ion-card-header>
+      <ion-card-content>
+        <p>{{ data }}</p>
+      </ion-card-content>
+    </ion-card>
+
+    <ion-card *ngIf="data1">
+      <ion-card-header>
+        <ion-card-title>上一次生成的报告内容</ion-card-title>
+      </ion-card-header>
+      <ion-card-content>
+        <p>{{ data1 }}</p>
+      </ion-card-content>
+    </ion-card>
+  </div>
+
+  <ng-template #noDataTemplate>
+    <p>没有找到相关记录。</p>
+  </ng-template>
+</ion-content>

+ 72 - 0
heartvoice-app/src/app/record/record.component.scss

@@ -0,0 +1,72 @@
+// record.component.scss
+ion-toolbar {
+    --background: #f8d7da; /* 顶部工具栏背景色 */
+    font-family: "宋体";
+  }
+ion-header {
+    --background: #f8d7da; /* 顶部工具栏背景色 */
+}
+
+ion-toolbar {
+    color: #333; /* 工具栏文字颜色 */
+}
+
+ion-content {
+    padding: 16px; /* 给内容添加内边距 */
+    --background: rgba(255, 221, 51, 0.1); /* 内容背景颜色 */
+
+    .record-container {
+        margin-top: 16px; /* 顶部间距 */
+    }
+
+    ion-card {
+        margin-bottom: 16px; /* 每个卡片之间的间距 */
+        background-color: #ffffff; /* 卡片背景颜色 */
+        border-radius: 8px; /* 圆角 */
+        box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* 阴影效果 */
+        
+        ion-card-header {
+            background-color: #f8d7da; /* 卡片标题背景颜色 */
+            border-top-left-radius: 8px; /* 圆角 */
+            border-top-right-radius: 8px; /* 圆角 */
+        }
+
+        ion-card-title {
+            font-family: "宋体"; /* 字体 */
+            font-weight: bold; /* 粗体 */
+            font-size: 18px; /* 字体大小 */
+            color: #333; /* 字体颜色 */
+        }
+
+        ion-card-content {
+            padding: 16px; /* 内容内边距 */
+            font-size: 16px; /* 内容字体大小 */
+            color: #666; /* 内容字体颜色 */
+        }
+    }
+
+    // 没有数据时的消息样式
+    ng-template {
+        text-align: center; /* 文本居中 */
+        font-size: 1.2rem; /* 加大字体 */
+        color: #888; /* 文本颜色 */
+        margin-top: 20px; /* 顶部间距 */
+    }
+}
+
+.fixed-button {
+    margin-top: 20px; /* 按钮与内容之间的间距 */
+    --background: #f8d7da; /* 按钮背景色 */
+    --color: #161616; /* 按钮字体颜色 */
+    border-radius: 5px; /* 圆角 */
+    font-weight: bold; /* 粗体 */
+
+    &:hover {
+        --background: rgba(255, 221, 51, 0.1); /* 悬停时按钮背景色 */
+    }
+
+    &:active {
+        --background: #f8d7da; /* 点击时按钮背景色 */
+        --color: #f96372; /* 点击时按钮字体颜色 */
+    }
+}

+ 22 - 0
heartvoice-app/src/app/record/record.component.spec.ts

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

+ 60 - 0
heartvoice-app/src/app/record/record.component.ts

@@ -0,0 +1,60 @@
+import { CommonModule } from '@angular/common';
+import { Component, OnInit } from '@angular/core';
+import { IonicModule } from '@ionic/angular';
+import { CloudUser,Cloudmy } from 'src/lib/ncloud';
+@Component({
+  selector: 'app-record',
+  templateUrl: './record.component.html',
+  styleUrls: ['./record.component.scss'],
+  standalone: true,
+  imports: [IonicModule,CommonModule],
+})
+export class RecordComponent  implements OnInit {
+  data1: any;
+  data: any;
+
+  constructor() {}
+
+  async ngOnInit() {
+    const currentUser = new CloudUser();
+    const userId = currentUser.toPointer();
+
+    // 查询 Userresponse 表
+    const query = new Cloudmy('Userresponse');
+    query.equalTo('user', userId);
+    query.descending('createdAt');
+
+    try {
+      const latestResponse = await query.first(); // 等待 Promise 解析
+
+      if (latestResponse && latestResponse.mbtiType) {
+        this.data = latestResponse.mbtiType;
+        console.log('MBTI 类型:', this.data);
+      } else {
+        console.log('没有找到 MBTI 类型');
+      }
+    } catch (error) {
+      console.error('查询 Userresponse 时出错:', error);
+    }
+
+    // 查询 PersonalityReport 表
+    const query1 = new Cloudmy('PersonalityReport');
+    query1.equalTo('user', userId);
+    query1.descending('createdAt');
+
+    try {
+      const latestResponse1 = await query1.first(); // 等待 Promise 解析
+
+      if (latestResponse1 && latestResponse1.reportContent) {
+        this.data1 = latestResponse1.reportContent; // 注意这里是 latestResponse1
+        console.log('报告内容:', this.data1);
+      } else {
+        console.log('没有找到报告内容');
+      }
+    } catch (error) {
+      console.error('查询 PersonalityReport 时出错:', error);
+    }
+  }
+  }
+
+

+ 42 - 4
heartvoice-app/src/app/report/report.component.ts

@@ -1,7 +1,7 @@
 import { Component, OnInit } from '@angular/core';
 import { ActivatedRoute } from '@angular/router';
 import { Cloudmy, Cloudget, Cloudupdate } from 'src/lib/ncloud';
-import { CloudUser } from 'src/lib/ncloud';
+import { CloudUser,CloudObject } from 'src/lib/ncloud';
 import Sentiment from 'sentiment';
 import axios from 'axios';
 import md5 from 'md5';
@@ -25,6 +25,7 @@ export class ReportComponent implements OnInit {
   data4: any;
   data5: any;
   responseMsg: string = "";
+ 
 
   constructor(private route: ActivatedRoute) {}
 
@@ -205,10 +206,47 @@ export class ReportComponent implements OnInit {
       let completion = new FmodeChatCompletion([
         { role: "user", content: PromptTemplate }
       ]);
-
+      let saveTimeout: any; // 定义一个变量来存储定时器
       completion.sendCompletion().subscribe((message: any) => {
-        console.log(message.content);
-        this.responseMsg = message.content;
+        //console.log(message.content);
+        this.responseMsg = message.content; 
+        // 清除之前的定时器
+    if (saveTimeout) {
+      clearTimeout(saveTimeout);
+  }
+
+  // 设置新的定时器
+  saveTimeout = setTimeout(() => { 
+      let responseMsgs = this.responseMsg;  
+      console.log(responseMsgs); // 打印最新的响应消息以供调试
+
+      let consult = new CloudObject("PersonalityReport");
+      let now = new Date();
+      let currentUser = new CloudUser();
+      let dateStr = `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()}`;
+      
+      let ACL: any = {
+          "*": { read: true, write: true }
+      };
+    
+      consult.set({
+          user: currentUser.toPointer(),
+          reportContent: responseMsgs, 
+          createdAt: dateStr,
+          // ACL: ACL
+      });
+    
+      consult.save().then(() => {
+          console.log('数据保存成功');
+      }).catch((error) => {
+          console.error('保存时出错:', error);
+      });
+  }, 20000); // 延迟 20 秒后保存
+
+
+
+
+
       });
     } else {
       console.warn("发送消息失败: 数据未完全赋值", {

+ 30 - 0
heartvoice-app/src/app/review-display/review-display.component.html

@@ -0,0 +1,30 @@
+<ion-header>
+  <ion-toolbar>
+    <ion-title>用户评价</ion-title>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content *ngIf="!loading; else loadingTemplate">
+  <ion-card *ngFor="let review of reviews">
+    <ion-card-header>
+      <ion-card-title>{{ review.username }}</ion-card-title>
+    </ion-card-header>
+    <ion-card-content>
+      <p>评论:</p>
+      <p>{{ review.get('feedbackContent') }}</p>
+      <ion-card-subtitle>星级: <app-rating-star [rating]="review.get('star')"></app-rating-star></ion-card-subtitle>
+      <p>发布时间:</p>
+      <p>{{ review.createdAt | date:'medium' }}</p>
+    </ion-card-content>
+  </ion-card>
+</ion-content>
+
+  <ion-button expand="full" (click)="goToReviewPage()" class="fixed-button">
+    我要评价
+  </ion-button>
+
+
+
+<ng-template #loadingTemplate>
+  <p>加载中...</p>
+</ng-template>

+ 58 - 0
heartvoice-app/src/app/review-display/review-display.component.scss

@@ -0,0 +1,58 @@
+// review-display.component.scss
+
+ion-toolbar {
+    --background: #f8d7da; /* 顶部工具栏背景色 */
+}
+
+ion-content {
+    padding: 16px; // 给内容添加内边距
+    --background: rgba(247, 246, 243, 0.1); // 内容背景颜色
+
+    ion-card {
+        margin-bottom: 16px; // 每个评论卡片之间的间距
+        --background: rgba(242, 229, 173, 0.1); // 卡片背景颜色
+        border-radius: 8px; // 圆角
+        box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); // 阴影效果
+
+        ion-card-header {
+            ion-card-title {
+                font-weight: bold; // 粗体
+                font-size: 20px ; // 字体大小
+                color: #333; // 字体颜色
+            }
+        }
+
+        p {
+            font-weight: bold;
+            font-family: "宋体" !important;
+            font-size: 20px !important; // 评论内容的字体大小
+            color: #666 !important; // 评论内容的颜色
+        }
+
+        ion-card-subtitle {
+            font-weight: bold;
+            font-family: "宋体" !important;
+            font-size: 20px !important;
+        }
+    }
+}
+
+ng-template {
+    text-align: center; // 加载中时的文本居中
+    font-size: 1.2rem; // 加载中文本的字体大小
+    color: #666; // 加载中文本的颜色
+}
+
+.fixed-button {
+    --background: #f8d7da; /* 按钮背景色 */
+    --color: #161616; /* 点击时按钮字体颜色 */
+
+    &:hover {
+        --background: rgba(255, 221, 51, 0.1); /* 悬停时按钮背景色 */
+    }
+
+    &:active {
+        --background: #f8d7da; /* 点击时按钮背景色 */
+        --color: #f96372; /* 点击时按钮字体颜色 */
+    }
+}

+ 22 - 0
heartvoice-app/src/app/review-display/review-display.component.spec.ts

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

+ 69 - 0
heartvoice-app/src/app/review-display/review-display.component.ts

@@ -0,0 +1,69 @@
+import { Component, OnInit ,ViewEncapsulation} from '@angular/core';
+import { CloudObject, CloudQuery } from 'src/lib/ncloud';
+import { IonicModule } from '@ionic/angular';
+import { CommonModule } from '@angular/common';
+import { Router } from '@angular/router';
+import { RatingStarComponent } from '../rating-star/rating-star.component';
+
+export interface Review extends CloudObject {
+    username?: string; // 可选的 username 属性
+    createdAt: string; // 可选的 createdAt 属性
+}
+
+@Component({
+    selector: 'app-review-display',
+    templateUrl: './review-display.component.html',
+    styleUrls: ['./review-display.component.scss'],
+    encapsulation: ViewEncapsulation.None, // 取消封装
+    standalone: true,
+    imports: [IonicModule, CommonModule, RatingStarComponent],
+})
+export class ReviewDisplayComponent implements OnInit {
+    reviews: Array<Review> = []; // 使用新的 Review 接口
+    loading: boolean = true; // 添加一个加载状态
+
+    constructor(private router: Router) {}
+
+    ngOnInit() {
+        // 页面加载后,运行加载评论列表的函数
+        this.loadReviews();
+    }
+
+    async loadReviews() {
+        this.loading = true; // 开始加载
+        let query = new CloudQuery("Review");
+        query.include('user'); // 加载关联的 _User 数据
+
+        try {
+            this.reviews = await query.find() as Array<Review>;
+            console.log(this.reviews); // 输出评论列表,检查每个评论的结构
+
+            // 遍历评论列表,获取每个评论的用户信息
+            for (const review of this.reviews) {
+                const user = review.get('user'); // 获取 user 对象
+                console.log(user); // 检查 user 的内容
+                
+                if (user) {
+                    // 直接访问 user 对象的 username 属性
+                    review.username = user.username; // 获取 username
+                } else {
+                    review.username = '匿名用户'; // 如果没有用户信息,设置为默认值
+                }
+            }
+
+            console.log(this.reviews); // 输出包含用户名的评论列表
+        } catch (error) {
+            console.error('加载评论时出错:', error);
+        } finally {
+            this.loading = false; // 加载完成
+        }
+    }
+
+    // 跳转到评价页面的函数
+    goToReviewPage() {
+        this.router.navigate(['/tabs/review']);
+    }
+}
+
+
+

+ 17 - 104
heartvoice-app/src/app/review/review.component.html

@@ -1,112 +1,25 @@
 <ion-header>
   <ion-toolbar>
-    <ion-title>用户评价</ion-title>
+    <ion-title>评价心语</ion-title>
   </ion-toolbar>
 </ion-header>
+<img src="assets/img/9.jpg" alt="  " width="100%" height="200"/> 
+<ion-content class="ion-padding">
+  <ion-item>
+    <ion-label position="floating">请输入对心语的评价</ion-label>
+    <ion-input [(ngModel)]="userInput" type="text"></ion-input>
+  </ion-item>
 
-<ion-content>
-  <ion-card>
-    <ion-card-header>
-      <ion-card-title>最新评价</ion-card-title>
-    </ion-card-header>
-    <ion-card-content>
-      <ion-list>
-        <ion-item>
-          <ion-avatar slot="start">
-            <img src="assets/img/1.png" alt="用户1" width="50" height="50"/>
-          </ion-avatar>
-          <ion-label>
-            <h2>用户234253</h2>
-            <p>这个平台让我感到被理解,帮助我缓解了很多压力。</p>
-          </ion-label>
-        </ion-item>
-        <ion-item>
-          <ion-avatar slot="start">
-            <img src="assets/img/3.jpg" alt="用户2" width="50" height="50"/>
-          </ion-avatar>
-          <ion-label>
-            <h2>北方的好吃不</h2>
-            <p>性格测试很有趣,聊天服务也很贴心。</p>
-          </ion-label>
-        </ion-item>
-        <ion-item>
-          <ion-avatar slot="start">
-            <img src="assets/img/1.png" alt="用户3" width="50" height="50"/>
-          </ion-avatar>
-          <ion-label>
-            <h2>含英咀华</h2>
-            <p>虽然AI的回答有时不够准确,但整体体验还是很不错的,值得一试。</p>
-          </ion-label>
-        </ion-item>
-        <ion-item>
-          <ion-avatar slot="start">
-            <img src="assets/img/2.png" alt="用户4" width="50" height="50"/>
-          </ion-avatar>
-          <ion-label>
-            <h2>还挺好玩</h2>
-            <p>希望能增加更多的测试类型,整体体验还是很好的,期待后续更新!</p>
-          </ion-label>
-        </ion-item>
-        <ion-item>
-          <ion-avatar slot="start">
-            <img src="assets/img/6.png" alt="用户5" width="50" height="50"/>
-          </ion-avatar>
-          <ion-label>
-            <h2>高大哥二狗狗</h2>
-            <p>这个平台让我意识到心理健康的重要性,推荐给身边的朋友们!</p>
-          </ion-label>
-        </ion-item>
-        <ion-item>
-          <ion-avatar slot="start">
-            <img src="assets/img/7.png" alt="用户6" width="50" height="50"/>
-          </ion-avatar>
-          <ion-label>
-            <h2>用户5243255</h2>
-            <p>整体体验非常好,但希望能有更多的互动活动,增加用户粘性。</p>
-          </ion-label>
-          </ion-item>
-          <ion-item>
-            <ion-avatar slot="start">
-              <img src="assets/img/4.jpg" alt="用户6" width="50" height="50"/>
-            </ion-avatar>
-            <ion-label>
-              <h2>梵蒂冈</h2>
-              <p>界面简洁友好,使用起来非常方便,性格测试的结果让我受益匪浅。</p>
-            </ion-label>
-            </ion-item>
-            <ion-item>
-              <ion-avatar slot="start">
-                <img src="assets/img/5.jpg" alt="用户6" width="50" height="50"/>
-              </ion-avatar>
-              <ion-label>
-                <h2>用户9888755</h2>
-                <p>AI聊天服务让我感到很放松,随时随地都能与它交流,缓解了我很多压力。。</p>
-              </ion-label>
-              </ion-item>
-              <ion-item>
-                <ion-avatar slot="start">
-                  <img src="assets/img/1.png" alt="用户6" width="50" height="50"/>
-                </ion-avatar>
-                <ion-label>
-                  <h2>感觉都好看</h2>
-                  <p>我在这里找到了一个可以倾诉的地方,感觉很温暖,感谢这个平台!。</p>
-                </ion-label>
-                </ion-item>
-      </ion-list>
-      <h2>我要评分</h2>
-      <h2>星级:</h2>
-      <edit-rating-star 
-  [score]="currentScore" 
-  [scoreMax]="5" 
-  (onScoreChange)="handleScoreChange($event)">
+<h2>星级:</h2>
+<edit-rating-star 
+[score]="currentScore" 
+[scoreMax]="5" 
+(onScoreChange)="handleScoreChange($event)">
 </edit-rating-star>
-      <ion-button expand="full" routerLink="/chat">提交</ion-button>
-    </ion-card-content>
-  </ion-card>
+
+
+  <ion-button expand="full" (click)="submitFeedback()" class="ion-margin-top" routerLink="/chat">
+    <ion-icon slot="start" name="send"></ion-icon>一键发表
+  </ion-button>
 </ion-content>
 
-<ion-footer>
-  <ion-toolbar>
-    <ion-title id="a">© 2024 心理健康服务平台</ion-title>
-  </ion-toolbar>
-</ion-footer>

+ 39 - 82
heartvoice-app/src/app/review/review.component.scss

@@ -1,86 +1,43 @@
 ion-toolbar {
-    --background: #f8d7da; /* 顶部工具栏背景色 */
+  --background: #f8d7da; /* 顶部工具栏背景色 */
+  font-family: "宋体";
+}
+
+ion-content {
+  padding: 16px; /* 给内容添加内边距 */
+  --background: rgba(255, 221, 51, 0.1); /* 内容背景颜色 */
+
+  ion-item {
+      margin-bottom: 16px; /* 每个输入项之间的间距 */
+      background-color: #ffffff; /* 输入项背景颜色 */
+      border-radius: 8px; /* 圆角 */
+      box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* 阴影效果 */
   }
-  
-  ion-content {
-      --background: #eefae7; /* 设置背景颜色 */
-      padding: 20px; /* 添加内边距 */
-      display: flex;
-      flex-direction: column;
-      align-items: center; /* 居中对齐内容 */
-    }
-    ion-card {
-      width: 100%; /* 卡片宽度 */
-      max-width: 600px; /* 最大宽度 */
-      margin: 20px 0; /* 卡片的上下外边距 */
-      border-radius: 10px; /* 卡片圆角 */
-      box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); /* 卡片阴影效果 */
-    }
-    
-    ion-card-header {
-      background-color: #f2f6f2; /* 卡片头部背景色 */
-      border-top-left-radius: 10px; /* 左上角圆角 */
-      border-top-right-radius: 10px; /* 右上角圆角 */
-    }
-    
-    ion-card-title {
-      font-size: 22px; /* 标题字体大小 */
-      color: #333; /* 标题颜色 */
-    }
-    
-    ion-item {
-      margin: 10px 0; /* 列表项的上下外边距 */
-      border-radius: 8px; /* 列表项圆角 */
-      background-color: rgba(255, 221, 51, 0.1); /* 列表项背景色 */
-      box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); /* 列表项阴影效果 */
-    }
-    ion-title{
-      font-family: "宋体";
-      font-weight: bold;
-      font-size: 30px;
-      text-align: center; /* 文本居中 */
-    }
-  
-    ion-avatar {
-      border-radius: 50%; /* 头像圆形 */
-    }
-    
-    h2 {
-      font-size: 18px; /* 小标题字体大小 */
-      color: #444; /* 小标题颜色 */
-      margin: 5px 0; /* 小标题的上下外边距 */
-    }
-    
-    p {
-      font-size: 14px; /* 段落字体大小 */
-      color: #666; /* 段落颜色 */
-    }
-    
-    edit-rating-star {
-      margin: 20px 0; /* 评分组件的上下外边距 */
-    }
-    
-    ion-button {
-      margin-top: 16px; /* 按钮的顶部外边距 */
-      background-color: #f8d7da; /* 按钮背景色 */
-      --color: #161616; /* 点击时按钮字体颜色 */
-      border-radius: 8px; /* 按钮圆角 */
-    }
-  
-  ion-button:hover {
-    --background: rgba(255, 221, 51, 0.1); /* 悬停时按钮背景色 */
+   h2{
+    font-family: "宋体";
+   }
+  ion-label {
+      color: #333; /* 标签颜色 */
   }
-  ion-button:active {
-    --background: #f8d7da; /* 点击时按钮背景色 */
-    --color: #f96372; /* 点击时按钮字体颜色 */
+
+  ion-input {
+      font-size: 16px; /* 输入框字体大小 */
+      color: #666; /* 输入框字体颜色 */
   }
-    
-    ion-footer {
-      background-color: #fff; /* 页脚背景色 */
-      border-top: 1px solid #ccc; /* 页脚顶部边框 */
-    }
-    
-    ion-title {
-      font-size: 16px; /* 页脚标题字体大小 */
-      color: #333; /* 页脚标题颜色 */
-    }
+
+  ion-button {
+      margin-top: 16px; /* 按钮与输入框之间的间距 */
+      --background: #f8d7da; /* 按钮背景色 */
+      --color: rgb(9, 9, 9); /* 按钮文本颜色 */
+      border-radius: 5px; /* 按钮圆角 */
+      text-align: center;
+
+      &:hover {
+          --background: rgba(255, 221, 51, 0.1); /* 悬停时按钮背景色 */
+      }
+
+      &:active {
+          --background: rgba(255, 221, 51, 0.1); /* 点击时按钮背景色 */
+      }
+  }
+}

+ 53 - 11
heartvoice-app/src/app/review/review.component.ts

@@ -1,29 +1,71 @@
 import { Component,OnInit } from '@angular/core';
-import { IonHeader, IonToolbar, IonTitle, IonContent, IonButton, IonList, IonItem, IonAvatar, IonLabel, IonCardHeader, 
-  IonCardContent, IonInput, IonFooter, IonButtons, IonCard, IonCardTitle } from '@ionic/angular/standalone';
 import { ExploreContainerComponent } from '../explore-container/explore-container.component';
 import { EditRatingStarComponent } from '../edit-rating-star/edit-rating-star.component';
-
+import { IonicModule } from '@ionic/angular';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms'; // 导入 FormsModule
+import { Router } from '@angular/router';
+import { CloudUser,CloudObject } from 'src/lib/ncloud';
 @Component({
   selector: 'app-review',
   templateUrl: './review.component.html',
   styleUrls: ['./review.component.scss'],
   standalone: true,
-  imports: [IonHeader,IonToolbar, IonTitle, IonContent,IonList,IonItem,IonAvatar,IonLabel,IonCard,IonCardHeader,IonCardContent,
-    IonCardTitle,IonInput,IonFooter,IonButtons,IonButton,ExploreContainerComponent,EditRatingStarComponent,]
+  imports: [ExploreContainerComponent,EditRatingStarComponent,IonicModule,CommonModule,FormsModule]
 })
 export class ReviewComponent  implements OnInit {
+  userInput: string = '';
+submittedFeedback: string | null = null;
+currentScore: number = 0; // 初始分值
+
+handleScoreChange(newScore: number) {
+  this.currentScore = newScore;
+  console.log('新分值:', newScore); // 处理分值变化
+}
+
+submitFeedback() {
+  this.submittedFeedback = this.userInput;
+  console.log(this.userInput); // 打印输入框的数据
+  
+  let consult = new CloudObject("Review");
+  // 设置聊天记录的基本信息
+  let now = new Date();
+  let currentUser = new CloudUser();
+  let dateStr = `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()}`;
+  
+  // 对象权限的精确制定
+  let ACL: any = {
+    "*": { read: true, write: true }
+  };
+
+  consult.set({
+    user: currentUser.toPointer(),
+    feedbackContent: this.userInput, 
+    createdAt: dateStr, // 可以加上创建时间
+    star: this.currentScore,
+
+
+    // ACL: ACL
+  });
+
+  // 调用 save 方法来保存数据
+  consult.save().then(() => {
+    console.log('反馈保存成功');
+    this.userInput = ''; // 清空输入框
+    this.router.navigate(['/tabs/review-display']); // 跳转到显示页面
+  }).catch((error) => {
+    console.error('保存反馈时出错:', error);
+  });
+
+      this.userInput = ''; // 清空输入框
+  this.router.navigate(['/tabs/review-display']);
+}
 
-  currentScore: number = 0; // 初始分值
 
-  handleScoreChange(newScore: number) {
-    this.currentScore = newScore;
-    console.log('新分值:', newScore); // 处理分值变化
-  }
 
   ngOnInit() {}
 
-  constructor() {}
+  constructor(private router: Router) {}
 
 
 }

+ 4 - 4
heartvoice-app/src/app/tab1/tab1.page.html

@@ -14,10 +14,10 @@
 <ion-content>
   <!-- 主横幅区域 -->
     <ion-row>
-      <img src="assets/img/2.png" alt="心理健康" width="130" height="70" />
+      <img src="assets/img/2.png" alt="心理健康" width="140" height="80" />
       <h2>关注你的心理健康</h2>
-      <p>个性化心理支持随时随地<strong>开始测试</strong></p>
-      <img src="assets/img/7.png" alt="心理支持" width="140" height="70" />
+      <p>个性化心理支持,随时随地<strong>开始测试</strong></p>
+      <img src="assets/img/7.png" alt="心理支持" width="170" height="90" />
   </ion-row>
 
   <!-- 核心功能介绍 -->
@@ -56,7 +56,7 @@
           <ion-card-content>
             你的反馈将帮助我们不断改善服务。
           </ion-card-content>
-          <ion-button expand="full" routerLink="/feedback">提供反馈</ion-button>
+          <ion-button expand="full" (click)="feedback()">提供反馈</ion-button>
         </ion-card>
       </ion-col>
     </ion-row>

+ 2 - 1
heartvoice-app/src/app/tab1/tab1.page.scss

@@ -20,11 +20,12 @@ h2 {
   font-size: 1.5rem; /* 主标题字体大小 */
   margin: 16px 0; /* 主标题上下间距 */
   text-align: center; /* 主标题居中 */
+  font-family: "宋体";
 }
 
 p {
   color: #666; /* 副标题颜色 */
-  font-size: 1rem; /* 副标题字体大小 */
+  font-size: 1.5rem; /* 副标题字体大小 */
   margin-bottom: 16px; /* 副标题下方间距 */
   text-align: center; /* 副标题居中 */
 }

+ 3 - 0
heartvoice-app/src/app/tab1/tab1.page.ts

@@ -29,6 +29,9 @@ export class Tab1Page {
    goTo(){
     this.router.navigate(['/tabs/interlocution'])
    }
+   feedback(){
+    this.router.navigate(['/tabs/feedback'])
+   }
    liaoTian(){
      this.openChat()
     // this.router.navigate(['/tabs/gexinhualiaotian'])

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

@@ -8,10 +8,8 @@
   <p class="container">请登入</p>
 }
 @if(currentUser?.id) {
-
   <div class="button-container">
     <ion-button (click)="openModal()">生成报告</ion-button>
   </div>
-
 }
 </ion-card>

+ 2 - 2
heartvoice-app/src/app/tab2/tab2.page.scss

@@ -6,7 +6,7 @@ ion-toolbar {
 
 ion-button {
   margin-top: 16px; /* 按钮的顶部外边距 */
-  --background: #f8d7da; /* 按钮背景色 */
+  --background: rgba(255, 221, 51, 0.1);  /* 按钮背景色 */
   font-family: "宋体";
   font-weight: bold;
   font-size: 20px;
@@ -23,7 +23,7 @@ ion-button:hover {
   --background: rgba(255, 221, 51, 0.1); /* 悬停时按钮背景色 */
 }
 ion-button:active {
-  --background: #f8d7da; /* 点击时按钮背景色 */
+  --background: rgba(255, 221, 51, 0.1); 
   --color: #f96372; /* 点击时按钮字体颜色 */
 }
   

+ 1 - 0
heartvoice-app/src/app/tab3/tab3.page.html

@@ -60,6 +60,7 @@
     @if(currentUser?.id) {
       <ion-button expand="block" (click)="review()" >用户评价</ion-button>
       <ion-button expand="block" (click)="feedback()" >提交反馈</ion-button>
+      <ion-button expand="block" (click)="record()" >历史记录</ion-button>
     }
   </ion-card>
 }

+ 3 - 1
heartvoice-app/src/app/tab3/tab3.page.scss

@@ -29,7 +29,9 @@ ion-toolbar {
     margin: 10px; /* 外边距 */
     box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); /* 阴影效果 */
   }
-  
+  ion-label {
+    --background: rgba(247, 238, 193, 0.1);
+  }
   /* 列表内容的样式 */
   ion-label h2 {
     font-size: 18px; /* 标题字体大小 */

+ 4 - 1
heartvoice-app/src/app/tab3/tab3.page.ts

@@ -25,7 +25,10 @@ export class Tab3Page {
     }, 2000);
   }
   review(){
-    this.router.navigate(['/tabs/review'])
+    this.router.navigate(['/tabs/review-display'])
+   }
+   record(){
+    this.router.navigate(['/tabs/record'])
    }
    feedback(){
     this.router.navigate(['/tabs/feedback'])

+ 10 - 0
heartvoice-app/src/app/tabs/tabs.routes.ts

@@ -41,6 +41,11 @@ export const routes: Routes = [
         loadComponent: () =>
           import('../review/review.component').then((m) => m.ReviewComponent),
       },
+      {
+        path: 'review-display',
+        loadComponent: () =>
+          import('../review-display/review-display.component').then((m) => m.ReviewDisplayComponent),
+      },
       {
         path: 'feedback',
         loadComponent: () =>
@@ -51,6 +56,11 @@ export const routes: Routes = [
         loadComponent: () =>
           import('../report/report.component').then((m) => m.ReportComponent),
       },
+      {
+        path: 'record',
+        loadComponent: () =>
+          import('../record/record.component').then((m) => m.RecordComponent),
+      },
       {
         path: '',
         redirectTo: '/tabs/tab1',

+ 26 - 20
heartvoice-app/src/lib/ncloud.ts

@@ -88,25 +88,30 @@ export class CloudQuery {
         this.className = className;
     }
 
-    include(...fileds:string[]) {
-        this.queryParams["include"] = fileds;
+    include(...fields: string[]) {
+        this.queryParams["include"] = fields;
     }
+
     greaterThan(key: string, value: any) {
+        if (!this.queryParams["where"]) this.queryParams["where"] = {};
         if (!this.queryParams["where"][key]) this.queryParams["where"][key] = {};
         this.queryParams["where"][key]["$gt"] = value;
     }
 
     greaterThanAndEqualTo(key: string, value: any) {
+        if (!this.queryParams["where"]) this.queryParams["where"] = {};
         if (!this.queryParams["where"][key]) this.queryParams["where"][key] = {};
         this.queryParams["where"][key]["$gte"] = value;
     }
 
     lessThan(key: string, value: any) {
+        if (!this.queryParams["where"]) this.queryParams["where"] = {};
         if (!this.queryParams["where"][key]) this.queryParams["where"][key] = {};
         this.queryParams["where"][key]["$lt"] = value;
     }
 
     lessThanAndEqualTo(key: string, value: any) {
+        if (!this.queryParams["where"]) this.queryParams["where"] = {};
         if (!this.queryParams["where"][key]) this.queryParams["where"][key] = {};
         this.queryParams["where"][key]["$lte"] = value;
     }
@@ -134,24 +139,23 @@ export class CloudQuery {
         return json || {};
     }
 
-    async find():Promise<Array<CloudObject>> {
+    async find(): Promise<Array<CloudObject>> {
         let url = `https://dev.fmode.cn/parse/classes/${this.className}?`;
 
-        let queryStr = ``
-        Object.keys(this.queryParams).forEach(key=>{
+        // 构建查询字符串
+        let queryStr = '';
+        Object.keys(this.queryParams).forEach(key => {
             let paramStr = JSON.stringify(this.queryParams[key]);
-            if(key=="include"){
-                paramStr = this.queryParams[key]?.join(",")
+            if (key === "include") {
+                paramStr = this.queryParams[key]?.join(","); // 将数组转换为逗号分隔字符串
             }
-            if(queryStr) {
-                url += `${key}=${paramStr}`;
-            }else{
+            // 添加查询参数到 URL
+            if (queryStr) {
                 url += `&${key}=${paramStr}`;
+            } else {
+                url += `${key}=${paramStr}`;
             }
-        })
-        // if (Object.keys(this.queryParams["where"]).length) {
-            
-        // }
+        });
 
         const response = await fetch(url, {
             headers: {
@@ -165,12 +169,14 @@ export class CloudQuery {
         });
 
         const json = await response?.json();
-        let list = json?.results || []
-        let objList = list.map((item:any)=>this.dataToObj(item))
+        console.log(json); // 检查 API 返回的数据结构
+        let list = json?.results || [];
+        let objList = list.map((item: any) => this.dataToObj(item));
         return objList || [];
-    }
 
 
+    }
+
     async first() {
         let url = `https://dev.fmode.cn/parse/classes/${this.className}?`;
 
@@ -193,13 +199,13 @@ export class CloudQuery {
         const json = await response?.json();
         const exists = json?.results?.[0] || null;
         if (exists) {
-            let existsObject = this.dataToObj(exists)
+            let existsObject = this.dataToObj(exists);
             return existsObject;
         }
-        return null
+        return null;
     }
 
-    dataToObj(exists:any):CloudObject{
+    dataToObj(exists: any): CloudObject {
         let existsObject = new CloudObject(this.className);
         existsObject.set(exists);
         existsObject.id = exists.objectId;