Browse Source

newest file

17870762608 2 months ago
parent
commit
005cb0a230
42 changed files with 1000 additions and 294 deletions
  1. 16 0
      mcbridge-app/src/app/app.routes.ts
  2. BIN
      mcbridge-app/src/app/img/picture1.jpg
  3. 12 0
      mcbridge-app/src/app/knowledge/knowledge-page/knowledge-page.component.html
  4. 0 0
      mcbridge-app/src/app/knowledge/knowledge-page/knowledge-page.component.scss
  5. 22 0
      mcbridge-app/src/app/knowledge/knowledge-page/knowledge-page.component.spec.ts
  6. 61 0
      mcbridge-app/src/app/knowledge/knowledge-page/knowledge-page.component.ts
  7. 96 51
      mcbridge-app/src/app/tab1/tab1.page.html
  8. 93 24
      mcbridge-app/src/app/tab1/tab1.page.scss
  9. 40 18
      mcbridge-app/src/app/tab1/tab1.page.ts
  10. 4 4
      mcbridge-app/src/app/tab2/tab2.page.html
  11. 175 66
      mcbridge-app/src/app/tab2/tab2.page.ts
  12. 3 3
      mcbridge-app/src/app/tab3/tab3.page.html
  13. 25 0
      mcbridge-app/src/app/tab3/tab3.page.scss
  14. 5 0
      mcbridge-app/src/app/tabs/tabs.routes.ts
  15. 9 0
      mcbridge-app/src/app/test-chat-panel/test-chat-panel.component.html
  16. 3 0
      mcbridge-app/src/app/test-chat-panel/test-chat-panel.component.scss
  17. 22 0
      mcbridge-app/src/app/test-chat-panel/test-chat-panel.component.spec.ts
  18. 243 0
      mcbridge-app/src/app/test-chat-panel/test-chat-panel.component.ts
  19. 27 68
      mcbridge-app/src/app/test-page/test-page.page.html
  20. 26 54
      mcbridge-app/src/app/test-page/test-page.page.scss
  21. 115 4
      mcbridge-app/src/app/test-page/test-page.page.ts
  22. BIN
      mcbridge-app/src/assets/image/picture1.jpg
  23. BIN
      mcbridge-app/src/assets/image/tab1.医疗诊断.png
  24. BIN
      mcbridge-app/src/assets/image/tab1.相似症状查询.png
  25. BIN
      mcbridge-app/src/assets/image/tab13.png
  26. BIN
      mcbridge-app/src/assets/image/tab14.png
  27. BIN
      mcbridge-app/src/assets/image/tab15.png
  28. BIN
      mcbridge-app/src/assets/image/tab16.png
  29. BIN
      mcbridge-app/src/assets/image/tab1主页.jpg
  30. BIN
      mcbridge-app/src/assets/image/住院.png
  31. BIN
      mcbridge-app/src/assets/image/医护云桥tab2.jpg
  32. BIN
      mcbridge-app/src/assets/image/屏幕截图 2024-12-21 210934.png
  33. BIN
      mcbridge-app/src/assets/image/屏幕截图 2024-12-21 211238.png
  34. BIN
      mcbridge-app/src/assets/image/屏幕截图 2024-12-21 211252.png
  35. BIN
      mcbridge-app/src/assets/image/屏幕截图 2024-12-21 211316.png
  36. BIN
      mcbridge-app/src/assets/image/屏幕截图 2024-12-21 211345.png
  37. BIN
      mcbridge-app/src/assets/image/屏幕截图 2024-12-21 211429.png
  38. BIN
      mcbridge-app/src/assets/image/急诊.png
  39. BIN
      mcbridge-app/src/assets/image/门诊.png
  40. BIN
      mcbridge-app/src/assets/image/预防保健.png
  41. 1 1
      mcbridge-app/src/main.ts
  42. 2 1
      mcbridge-app/tsconfig.json

+ 16 - 0
mcbridge-app/src/app/app.routes.ts

@@ -9,4 +9,20 @@ export const routes: Routes = [
     path: 'test-page',
     loadComponent: () => import('./test-page/test-page.page').then( m => m.TestPagePage)
   },
+     // 聊天模块
+     {
+      path: "chat/session/role/:chatId",
+      loadComponent: () => import('./test-chat-panel/test-chat-panel.component').then(m => m.TestChatPanelComponent),
+      runGuardsAndResolvers: "always",
+  },
+  {
+      path: "chat/session/chat/:chatId",
+      loadComponent: () => import('./test-chat-panel/test-chat-panel.component').then(m => m.TestChatPanelComponent),
+      runGuardsAndResolvers: "always",
+  },
+  {
+      path: 'chat/pro/chat/:chatId',
+      redirectTo: '/chat/session/chat/:chatId',
+      pathMatch: 'full'
+    },
 ];

BIN
mcbridge-app/src/app/img/picture1.jpg


+ 12 - 0
mcbridge-app/src/app/knowledge/knowledge-page/knowledge-page.component.html

@@ -0,0 +1,12 @@
+<ion-content>
+  <ion-list>
+    <ion-item lines="none">
+      <div class="doctor-info">
+        <h1>{{ knowledge?.get('title') }}</h1>
+        <h3>{{ knowledge?.get('author') }}</h3>
+        <p>{{ knowledge?.get('content') }}</p>
+        <p><span>出处:</span> {{ knowledge?.get('source') }}</p>
+      </div>
+    </ion-item>
+  </ion-list>
+</ion-content>

+ 0 - 0
mcbridge-app/src/app/knowledge/knowledge-page/knowledge-page.component.scss


+ 22 - 0
mcbridge-app/src/app/knowledge/knowledge-page/knowledge-page.component.spec.ts

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

+ 61 - 0
mcbridge-app/src/app/knowledge/knowledge-page/knowledge-page.component.ts

@@ -0,0 +1,61 @@
+import { CommonModule } from '@angular/common';
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+import { IonContent } from '@ionic/angular/standalone';
+import { IonItem, IonList, IonThumbnail } from '@ionic/angular/standalone';
+import { CloudObject, CloudQuery } from 'src/lib/ncloud';
+
+@Component({
+  selector: 'app-knowledge-page',
+  templateUrl: './knowledge-page.component.html',
+  styleUrls: ['./knowledge-page.component.scss'],
+  standalone: true,
+  imports: [
+    CommonModule, IonList, IonItem, IonThumbnail, IonContent
+  ]
+})
+export class KnowledgePageComponent implements OnInit {
+
+  // constructor() { }
+
+  // ngOnInit() {
+  //   // 生命周期:页面加载后,运行医生列表加载函数
+  //   this.loadKnowledgeList()
+  // }
+
+  // // 创建用于数据列表存储的属性
+  // knowledgeList:Array<CloudObject> = []
+  // // 查询并加载医生列表的函数
+  // async loadKnowledgeList(){
+  //   let query = new CloudQuery("Knowledge");
+  //   query.equalTo("objectId", "4YJevU7uTe");
+  //   this.knowledgeList = await query.find()
+  // }
+
+  // 在构造函数中注入 ActivatedRoute
+  knowledge: CloudObject | null = null; // 初始化为 null
+
+  constructor(private route: ActivatedRoute) {}
+
+  ngOnInit() {
+    const objectId = this.route.snapshot.paramMap.get('id'); // 获取传递的 ObjectId
+    if (objectId) { // 检查 objectId 是否为 null
+      this.loadKnowledge(objectId); // 加载对应的知识信息
+    } else {
+      console.error("No ObjectId found in route parameters.");
+    }
+  }
+
+  async loadKnowledge(objectId: string) {
+    let query = new CloudQuery("Knowledge");
+    query.equalTo("title", objectId); // 根据 ObjectId 查询
+    const knowledge = await query.first(); // 获取单条记录
+    if (knowledge) {
+      this.knowledge = knowledge; // 将获取的知识信息赋值给 knowledge 属性
+    } else {
+      console.error("Knowledge not found for ObjectId:", objectId);
+      // 可以考虑在这里处理找不到知识的情况,比如导航到错误页面或者显示提示
+    }
+  }
+
+}

+ 96 - 51
mcbridge-app/src/app/tab1/tab1.page.html

@@ -1,45 +1,69 @@
-<ion-header [translucent]="true">  
-  <ion-toolbar>  
-    <ion-title>  
-      医护云桥APP 
-    </ion-title>  
-  </ion-toolbar>  
-</ion-header>  
+<ion-header [translucent]="true">
+  <ion-toolbar>
+    <ion-title>
+      医护云桥APP
+    </ion-title>
+  </ion-toolbar>
+</ion-header>
 
-<ion-content [fullscreen]="true">  
-  <!-- 添加搜索框 -->  
-  <ion-searchbar placeholder="搜索..." (ionInput)="search($event)"></ion-searchbar>  
-
-     <!-- 轮播图区域 -->
-     <div class="carousel-container">
-      <div class="carousel" [style.transform]="'translateX(-' + currentSlide * 100 + '%)'">
-        <div class="slide" *ngFor="let image of images">
-          <img [src]="image" alt="轮播图">
-        </div>
-      </div>
-    
-      <button class="prev" (click)="prevSlide()">&#10094;</button>
-      <button class="next" (click)="nextSlide()">&#10095;</button>
-    
-      <div class="dots">
-        <span class="dot" *ngFor="let image of images; let i = index" 
-              [class.active]="i === currentSlide" 
-              (click)="goToSlide(i)"></span>
-      </div>
-    </div>
+<ion-content [fullscreen]="true">
+  <!-- 添加搜索框 -->
+  <ion-searchbar placeholder="搜索..." (ionInput)="search($event)"></ion-searchbar>
 
   <!-- 按钮容器 -->
-  <div class="button-container">
+  <!-- <div class="button-container">
     <ion-button  size="large" color="light" (click)="goToPage1()">
       智能诊疗
     </ion-button>
     <ion-button  size="large" color="light" (click)="goToPage2()">
       相似症状图查询
     </ion-button>
+  </div> -->
+
+  <div class="button-row1">
+    <ion-button expand="full" fill="outline" color="light" (click)="goToPage1()">
+      <img src="../../assets/image/picture1.jpg" alt="报病查诊" class="button-icon" />
+      <!-- <span style="color: black;">智能诊疗</span> -->
+    </ion-button>
+    <ion-button expand="full" fill="outline" color="light" (click)="goToPage2()">
+      <img src="../../assets/image/屏幕截图 2024-12-21 211238.png" alt="预约记录" class="button-icon" />
+    </ion-button>
+    <ion-button expand="full" fill="outline" color="light" routerLink="/discussions">
+      <img src="../../assets/image/屏幕截图 2024-12-21 211252.png" alt="讨论记录" class="button-icon" />
+    </ion-button>
+  </div>
+
+  <div class="button-row2">
+    <ion-button expand="full" fill="outline" color="light" routerLink="/tabs/inquiry">
+      <img src="../../assets/image/屏幕截图 2024-12-21 211316.png" alt="报病查诊" class="button-icon" />
+    </ion-button>
+    <ion-button expand="full" fill="outline" color="light" routerLink="/appointments">
+      <img src="../../assets/image/屏幕截图 2024-12-21 211345.png" alt="预约记录" class="button-icon" />
+    </ion-button>
+    <ion-button expand="full" fill="outline" color="light" routerLink="/discussions">
+      <img src="../../assets/image/屏幕截图 2024-12-21 211429.png" alt="讨论记录" class="button-icon" />
+    </ion-button>
   </div>
 
-  <!-- 页面内容 -->  
-  <ion-card *ngFor="let item of items">  
+  <!-- 轮播图区域 -->
+  <div class="carousel-container">
+    <div class="carousel" [style.transform]="'translateX(-' + currentSlide * 100 + '%)'">
+      <div class="slide" *ngFor="let image of images">
+        <img [src]="image" alt="轮播图">
+      </div>
+    </div>
+
+    <button class="prev" (click)="prevSlide()">&#10094;</button>
+    <button class="next" (click)="nextSlide()">&#10095;</button>
+
+    <div class="dots">
+      <span class="dot" *ngFor="let image of images; let i = index" [class.active]="i === currentSlide"
+        (click)="goToSlide(i)"></span>
+    </div>
+  </div>
+
+  <!-- 页面内容 -->
+  <!-- <ion-card *ngFor="let item of items">  
     <img [src]="item.image" alt="{{ item.title }}" />  
     <ion-card-header>  
       <ion-card-title>{{ item.title }}</ion-card-title>   
@@ -53,26 +77,47 @@
     (onScoreChange)="handleScoreChange($event)">
   </edit-rating-star>
   
-  </ion-card>  
+  </ion-card>   -->
+
+  <!-- 知识模块 -->
+  <ion-card>
+    <ion-card-header>
+      <ion-card-title>热点咨询</ion-card-title>
+    </ion-card-header>
+    <ion-card-content>
+      <ion-list>
+        <ion-item *ngFor="let knowledge of knowledgeList" lines="none" class="doctor-item" (click)="goToKnowledge(knowledge)">
+          <ion-thumbnail slot="start">
+            <img [src]="knowledge.get('img')" [alt]="knowledge.get('title')" />
+          </ion-thumbnail>
+          <div class="doctor-info">
+            <h3>{{ knowledge.get('title') }}</h3>
+            <p>{{ knowledge?.get('author') }}</p>
+          </div>
+        </ion-item>
+      </ion-list>
+    </ion-card-content>
+  </ion-card>
+
 
-  <ion-list>  
-    <ion-item>  
-      <ion-label>  
-        <h2>关于我们</h2>  
-        <p>我们致力于为医护人员提供最佳支持,帮助他们更高效地工作。</p>  
-      </ion-label>  
-    </ion-item>  
-    <ion-item lines="none">  
-      <ion-label>  
-        <h2>联系我们</h2>  
-        <p>如果您有任何问题,请随时与我们联系!</p>  
-      </ion-label>  
-    </ion-item>  
-  </ion-list>  
+  <!-- <ion-list>
+    <ion-item>
+      <ion-label>
+        <h2>关于我们</h2>
+        <p>我们致力于为医护人员提供最佳支持,帮助他们更高效地工作。</p>
+      </ion-label>
+    </ion-item>
+    <ion-item lines="none">
+      <ion-label>
+        <h2>联系我们</h2>
+        <p>如果您有任何问题,请随时与我们联系!</p>
+      </ion-label>
+    </ion-item>
+  </ion-list>
 
-  <ion-footer>  
-    <ion-toolbar>  
-      <ion-title>© 2024 医护云桥</ion-title>  
-    </ion-toolbar>  
-  </ion-footer>  
+  <ion-footer>
+    <ion-toolbar>
+      <ion-title>© 2024 医护云桥</ion-title>
+    </ion-toolbar>
+  </ion-footer> -->
 </ion-content>

+ 93 - 24
mcbridge-app/src/app/tab1/tab1.page.scss

@@ -6,45 +6,88 @@ ion-searchbar {
     padding: 0 16px;  
     height: 50px;  
   }  
+
+  // 按钮区域
+    /* 按钮行样式 */
+    .button-row1 {
+      display: flex;
+      justify-content: space-between; /* 按钮均匀分布 */
+      flex-wrap: wrap; /* 允许换行 */
+      
+    }
+    
+    .button-row1 ion-button {
+      flex: 1; /* 使按钮均匀分布 */
+      margin: 10px 20px 0px 20px; /* 按钮之间的间距 */
+      flex-basis: 90px; /* 设置按钮的基础宽度 */
+      min-width: 90px; /* 设置按钮的最小宽度 */
+      max-width: 120px; /* 设置按钮的最大宽度 */
+    }
+  
+      /* 按钮行样式 */
+  .button-row2 {
+    display: flex;
+    justify-content: space-between; /* 按钮均匀分布 */
+    flex-wrap: wrap; /* 允许换行 */
+  }
+  
+  .button-row2 ion-button {
+    flex: 1; /* 使按钮均匀分布 */
+    margin: 4px; /* 按钮之间的间距 */
+    margin: 10px 20px 0px 20px; /* 按钮之间的间距 */
+    flex-basis: 90px; /* 设置按钮的基础宽度 */
+    min-width: 90px; /* 设置按钮的最小宽度 */
+    max-width: 50px; /* 设置按钮的最大宽度 */
+  }
   
+  .button-icon {
+    max-width: 100%; /* 确保图片不会超出按钮的宽度 */
+    height: auto; /* 保持图片的纵横比 */
+    // width: 300px;
+    // height: auto;
+  }
+  
+
+  // ...
   ion-card {  
-    margin: 10px;  
+    margin: 10px 0px 10px 0px;  
   }  
   
-  ion-card img {  
-    width: 400px;
-    height:auto;  
-    border-top-left-radius: 8px;  
-    border-top-right-radius: 8px;  
-  }  
+  // ion-card img {  
+  //   width: 400px;
+  //   height:auto;  
+  //   border-top-left-radius: 8px;  
+  //   border-top-right-radius: 8px;  
+  // }  
   
-  ion-item {  
-    --background: #ffffff;  
-    margin: 10px 0;  
-  }
+  // ion-item {  
+  //   --background: #ffffff;  
+  //   margin: 10px 0;  
+  // }
 
   ion-button{
     background-color: #f8f9fa;
   }
 
-  .button-container {
-    display: flex;
-    justify-content: space-between; /* 在按钮之间留出空间 */
-    margin-top: 10px; /* 可选,调整按钮的上边距 */
-  }
+  // .button-container {
+  //   display: flex;
+  //   justify-content: space-between; /* 在按钮之间留出空间 */
+  //   margin-top: 10px; /* 可选,调整按钮的上边距 */
+  //   margin-bottom: 10px;
+  // }
   
-  .button-container ion-button {
-    flex: 1; /* 每个按钮占据相同的空间 */
-    margin: 0 5px; /* 按钮之间的间距 */
-    color: #f8f9fa;
-    background-color: #f9faf8;
-  }
+  // .button-container ion-button {
+  //   flex: 1; /* 每个按钮占据相同的空间 */
+  //   margin: 0 5px; /* 按钮之间的间距 */
+  //   color: #f8f9fa;
+  //   background-color: #f9faf8;
+  // }
 
 // 轮播图区域
 .carousel-container {
   position: relative;
   max-width: 800px;
-  margin: 0 auto;
+  margin: 10px 10px 10px 10px;
   overflow: hidden;
 }
 
@@ -55,6 +98,7 @@ ion-searchbar {
 
 .slide {
   min-width: 100%;
+  height: 100px;
 }
 
 .slide img {
@@ -101,4 +145,29 @@ ion-searchbar {
 
 .dot.active {
   background: #717171;
-} 
+} 
+
+// 知识区域
+.doctor-item {
+  display: flex;
+  align-items: center;
+  // padding: 15px; /* 增加内边距 */
+  margin-bottom: 0px; /* 增加每个医生项之间的间距 */
+  border: 1px solid #e0e0e0; /* 添加边框 */
+  border-radius: 0px; /* 圆角边框 */
+  background-color: #f9f9f9; /* 背景颜色 */
+}
+
+.doctor-item ion-thumbnail {
+  margin-right: 15px; /* 增加缩略图和文本之间的间距 */
+}
+
+.doctor-info h3 {
+  margin: 0; /* 去掉默认的外边距 */
+  font-size: 1.2em; /* 调整字体大小 */
+}
+
+.doctor-info p {
+  margin: 5px 0; /* 增加段落之间的间距 */
+  color: #666; /* 调整字体颜色 */
+}

+ 40 - 18
mcbridge-app/src/app/tab1/tab1.page.ts

@@ -8,6 +8,8 @@ import { IonButton } from '@ionic/angular/standalone';
 import { IonIcon } from '@ionic/angular/standalone';
 import { addIcons } from 'ionicons';
 import { bagAddOutline, imagesOutline } from 'ionicons/icons';
+import { CloudObject, CloudQuery } from 'src/lib/ncloud';
+import { IonThumbnail } from '@ionic/angular/standalone';
 
 addIcons({ bagAddOutline, imagesOutline}) 
 @Component({  
@@ -20,7 +22,8 @@ addIcons({ bagAddOutline, imagesOutline})
     IonHeader, IonToolbar, IonTitle, IonContent, IonSearchbar,   
     IonCard, IonCardHeader, IonCardTitle, IonCardSubtitle, IonCardContent,   
     IonItem, IonLabel, IonList, IonFooter, ExploreContainerComponent ,
-    EditRatingStarComponent, IonButton, IonIcon
+    EditRatingStarComponent, IonButton, IonIcon,
+    IonThumbnail
   ],  
 })  
 export class Tab1Page {
@@ -70,6 +73,11 @@ export class Tab1Page {
     this.router.navigate(['/tabs/inquiry'])
   }
 
+  goToKnowledge(knowledge: CloudObject){
+    const objectId = knowledge.get('title'); // 假设你的主键字段为 'title'
+    this.router.navigate(['/tabs/knowledge-page', { id: objectId }]);
+  }
+
   currentScore: number = 0; // 初始分值
 
   handleScoreChange(newScore: number) {
@@ -77,23 +85,37 @@ export class Tab1Page {
     console.log('新分值:', newScore); // 处理分值变化
   }
 
-  items = [  
-    {  
-      title: '医疗技术',  
-      description: '我们提供最新的医疗设备和技术,帮助提高医护质量和效率。',  
-      image: 'https://tse3-mm.cn.bing.net/th/id/OIP-C.NHe6NqhU0qZ_qLO0DQ08pAHaFj?rs=1&pid=ImgDetMain'  
-    },  
-    {  
-      title: '健康管理',   
-      description: '专业的健康管理方案,确保您的身体健康得到最佳维护。',  
-      image: ''  
-    },  
-    {  
-      title: '护理服务',  
-      description: '我们提供全面的护理服务,为患者提供贴心的照顾。',  
-      image: 'https://tse1-mm.cn.bing.net/th/id/OIP-C.N6uSj6zCe2pDw7vA4fcYuAHaE7?w=214&h=180&c=7&r=0&o=5&dpr=1.5&pid=1.7'  
-    }  
-  ];  
+  // items = [  
+  //   {  
+  //     title: '医疗技术',  
+  //     description: '我们提供最新的医疗设备和技术,帮助提高医护质量和效率。',  
+  //     image: 'https://tse3-mm.cn.bing.net/th/id/OIP-C.NHe6NqhU0qZ_qLO0DQ08pAHaFj?rs=1&pid=ImgDetMain'  
+  //   },  
+  //   {  
+  //     title: '健康管理',   
+  //     description: '专业的健康管理方案,确保您的身体健康得到最佳维护。',  
+  //     image: ''  
+  //   },  
+  //   {  
+  //     title: '护理服务',  
+  //     description: '我们提供全面的护理服务,为患者提供贴心的照顾。',  
+  //     image: 'https://tse1-mm.cn.bing.net/th/id/OIP-C.N6uSj6zCe2pDw7vA4fcYuAHaE7?w=214&h=180&c=7&r=0&o=5&dpr=1.5&pid=1.7'  
+  //   }  
+  // ];  
+
+
+    ngOnInit() {
+      // 生命周期:页面加载后,运行医生列表加载函数
+      this.loadKnowledgeList()
+    }
+  
+    // 创建用于数据列表存储的属性
+    knowledgeList:Array<CloudObject> = []
+    // 查询并加载医生列表的函数
+    async loadKnowledgeList(){
+      let query = new CloudQuery("Knowledge");
+      this.knowledgeList = await query.find()
+    }
 
 
 

+ 4 - 4
mcbridge-app/src/app/tab2/tab2.page.html

@@ -15,13 +15,13 @@
     </ion-card-header>
     <ion-card-content>
       <ion-list>
-        <ion-item *ngFor="let doctor of doctorList" lines="none" class="doctor-item">
+        <ion-item (click)="openInquiry(expect)" *ngFor="let expect of expectList" lines="none" class="doctor-item">
           <ion-thumbnail slot="start">
-            <img [src]="doctor.get('avatar')" [alt]="doctor.get('name')" />
+            <img [src]="expect.get('avatar')" [alt]="expect.get('name')" />
           </ion-thumbnail>
           <div class="doctor-info">
-            <h3>{{ doctor.get('name') }} ({{ doctor.get('age') }}岁)</h3>
-            <p>{{ doctor.get('title') }}, {{ doctor.get('depart')?.name }}</p>
+            <h3>{{ expect.get('name') }} ({{ expect.get('age') }}岁)</h3>
+            <p>{{ expect.get('title') }} {{ expect.get('specialist') }}</p>
             <p>飞码互联网医院</p>
           </div>
         </ion-item>

+ 175 - 66
mcbridge-app/src/app/tab2/tab2.page.ts

@@ -14,7 +14,9 @@ import { CommonModule, DecimalPipe } from '@angular/common';
 import { addIcons } from 'ionicons';
 import { radioButtonOffOutline, reloadOutline, checkmarkCircleOutline, closeCircleOutline } from 'ionicons/icons';
 import { IonCard, IonCardContent, IonCardHeader, IonCardSubtitle, IonCardTitle, IonItem, IonList, IonThumbnail } from '@ionic/angular/standalone';
-import { CloudObject, CloudQuery } from 'src/lib/ncloud';
+import { CloudObject, CloudQuery, CloudUser } from 'src/lib/ncloud';
+import { openUserLoginModal } from 'src/lib/user/modal-user-login/modal-user-login.component';
+import { ChatPanelOptions, FmodeChat, FmodeChatMessage, openChatPanelModal } from 'fmode-ng';
 
 addIcons({radioButtonOffOutline, reloadOutline, checkmarkCircleOutline, closeCircleOutline}) 
 @Component({
@@ -33,83 +35,190 @@ export class Tab2Page {
 
   }
 
-  taskList:AgentTaskStep[] = []
+  // taskList:AgentTaskStep[] = []
 
-  wait(duration:number=1000){
-    return new Promise(resolve=>{
-      setTimeout(() => {
-          resolve(true)
-      }, duration);
-    })
-  }
+  // wait(duration:number=1000){
+  //   return new Promise(resolve=>{
+  //     setTimeout(() => {
+  //         resolve(true)
+  //     }, duration);
+  //   })
+  // }
 
-  shareData:any = {}
+  // shareData:any = {}
 
-  // 任务:完成故事意境描述及图像绘制
-  doPoemTask(){
-    // 产生: shareData.PictureDescResult 生成后描述
-    let task1 = TaskPoemPictureDesc({shareData:this.shareData,modalCtrl:this.modalCtrl});
-    // 产生: shareData.images 渲染后图片
-    let task2 = TaskPoemPictureCreate({shareData:this.shareData,modalCtrl:this.modalCtrl});
+  // // 任务:完成故事意境描述及图像绘制
+  // doPoemTask(){
+  //   // 产生: shareData.PictureDescResult 生成后描述
+  //   let task1 = TaskPoemPictureDesc({shareData:this.shareData,modalCtrl:this.modalCtrl});
+  //   // 产生: shareData.images 渲染后图片
+  //   let task2 = TaskPoemPictureCreate({shareData:this.shareData,modalCtrl:this.modalCtrl});
     
-    // 定义任务集
-    let PoemTaskList = [task1,task2]
-    // 传递给显示组件
-    this.taskList = PoemTaskList
-    // 开始执行任务
-    TaskExecutor(PoemTaskList)
-  }
+  //   // 定义任务集
+  //   let PoemTaskList = [task1,task2]
+  //   // 传递给显示组件
+  //   this.taskList = PoemTaskList
+  //   // 开始执行任务
+  //   TaskExecutor(PoemTaskList)
+  // }
 
 
-  testJSON(){
-    let string = `
-          ''''json
-              {
-                  "keshi": "神经内科",
-                  "sympList": [
-                      {
-                          "title": "偏头痛",
-                          "desc": "持续了2天的偏头疼",
-                          "duration": "2天"
-                      },
-                      {
-                          "title": "发冷",
-                          "desc": "感觉发冷,已经有一天",
-                          "duration": "1天"
-                      }
-                  ]
-              }
-              ''''
-      `
-      console.log(extactAndParseJsonFromString(string))
-  }
-  // 任务集:医疗问诊任务集 完成一次完整的门诊服务
-  doInqueryTask(){
-    let task1 = TaskInqueryUserStory({shareData:this.shareData,modalCtrl:this.modalCtrl});
-    let task2 = TaskInqueryDoctorQuestion({shareData:this.shareData,modalCtrl:this.modalCtrl});
-    let task3 = TaskInqueryUserAnswer({shareData:this.shareData,modalCtrl:this.modalCtrl});
-
-    // 定义任务集
-    let InquireServiceTaskList = [
-      task1,task2,task3
-    ]
-    // 传递给显示组件
-    this.taskList = InquireServiceTaskList
-    // 开始执行任务
-    TaskExecutor(InquireServiceTaskList)
-  }
+  // testJSON(){
+  //   let string = `
+  //         ''''json
+  //             {
+  //                 "keshi": "神经内科",
+  //                 "sympList": [
+  //                     {
+  //                         "title": "偏头痛",
+  //                         "desc": "持续了2天的偏头疼",
+  //                         "duration": "2天"
+  //                     },
+  //                     {
+  //                         "title": "发冷",
+  //                         "desc": "感觉发冷,已经有一天",
+  //                         "duration": "1天"
+  //                     }
+  //                 ]
+  //             }
+  //             ''''
+  //     `
+  //     console.log(extactAndParseJsonFromString(string))
+  // }
+  // // 任务集:医疗问诊任务集 完成一次完整的门诊服务
+  // doInqueryTask(){
+  //   let task1 = TaskInqueryUserStory({shareData:this.shareData,modalCtrl:this.modalCtrl});
+  //   let task2 = TaskInqueryDoctorQuestion({shareData:this.shareData,modalCtrl:this.modalCtrl});
+  //   let task3 = TaskInqueryUserAnswer({shareData:this.shareData,modalCtrl:this.modalCtrl});
+
+  //   // 定义任务集
+  //   let InquireServiceTaskList = [
+  //     task1,task2,task3
+  //   ]
+  //   // 传递给显示组件
+  //   this.taskList = InquireServiceTaskList
+  //   // 开始执行任务
+  //   TaskExecutor(InquireServiceTaskList)
+  // }
 
   ngOnInit() {
     // 生命周期:页面加载后,运行医生列表加载函数
-    this.loadDoctorList()
+    this.loadExpectList()
   }
 
   // 创建用于数据列表存储的属性
-  doctorList:Array<CloudObject> = []
+  expectList:Array<CloudObject> = []
   // 查询并加载医生列表的函数
-  async loadDoctorList(){
-    let query = new CloudQuery("Doctor");
-    this.doctorList = await query.find()
+  async loadExpectList(){
+    let query = new CloudQuery("Expect");
+    this.expectList = await query.find()
   }
 
-}
+
+     /** 示例:问诊根据expect拼接提示词 */
+     async openInquiry(expect:any){
+      // 验证用户登录
+      let currentUser = new CloudUser();
+      let userPrompt = ``
+      if(!currentUser?.id){
+        console.log("用户未登录,请登录后重试");
+        let user = await openUserLoginModal(this.modalCtrl);
+        if(!user?.id){
+          return
+        }
+        currentUser = user;
+      }
+
+      console.log(expect.get('depart'))
+      
+      if(currentUser?.get("realname")){
+        userPrompt += `当前来访的患者,姓名:${currentUser?.get("realname")}`
+      }
+      if(currentUser?.get("gender")){
+        userPrompt += `,性别:${currentUser?.get("gender")}`
+      }
+      if(currentUser?.get("age")){
+        userPrompt += `,年龄:${currentUser?.get("age")}`
+      }
+  
+  
+      localStorage.setItem("company","E4KpGvTEto")
+  
+      let consult = new CloudObject("Consultation")
+      let now = new Date();
+      let dateStr = `${now.getFullYear()}-${now.getMonth()+1}-${now.getDate()}`
+      // 对象权限的精确指定
+      let ACL:any = {
+        "*":{read:false,write:false}
+      }
+      if(currentUser?.id){
+        ACL[currentUser?.id] = {read:true,write:true}
+      }
+      consult.set({
+        title:`${expect.get('depart')?.name || ""}门诊记录${dateStr}-${expect?.get("name")}`,
+        expect:expect.toPointer(),
+        depart:{
+          __type:"Pointer",
+          className:"Department",
+          objectId:expect.get("depart")?.objectId
+        },
+        user:currentUser.toPointer(),
+        ACL:ACL
+      })
+  
+      let options:ChatPanelOptions = {
+        roleId:"2DXJkRsjXK",
+        onChatInit:(chat:FmodeChat)=>{
+          console.log("onChatInit");
+                console.log("预设角色",chat.role);
+                chat.role.set("name",expect?.get("name"));
+                chat.role.set("title",expect?.get("title"));
+                chat.role.set("desc",expect?.get("desc"));
+                chat.role.set("tags",expect?.get("qualifications"));
+                chat.role.set("avatar",expect?.get("avatar") || "../../assets/image/doctor7.png")
+                chat.role.set("prompt",`
+  # 角色设定
+  您是${expect?.get("name")},是一位${expect?.get("specialist")}医生,${expect?.get("desc")},年龄${expect?.get("age")}岁,需要完成有关本科室的门诊服务,无关科室的症状描述请委婉结束聊天并推荐患者去对应的科室。
+  
+  # 对话环节
+  0.导诊(根据用户基本情况,引导挂号合适的科室)
+  1.预设的问询方式(根据不同症状来问询具体的情况)
+  - 打招呼,以用户自述为主
+  - 当信息充足时候,确认用户症状对应的科室,并进入下一个环节
+  2.拓展的问询细节
+  例如:用户反映呼吸不畅,拓展出:是否咳嗽;是否感觉痛或者痒等其他需要的问题。
+  - 当问询细节补充完成后进入下一个环节
+  3.初步的诊断结果,并且同时列出检查检验项目
+  初步诊断:确定需要有哪些进一步检查
+  检查检验:获取医学客观数据
+  - 等待用户提交客观数据,进入下一阶段
+  4.给出诊断方案并给出处方
+  - 完成处方时,请在消息结尾附带: [处方完成]
+  
+  # 开始话语
+  当您准备好了,可以以一个医生的身份,先向来访的用户亲切地打招呼。
+  ${userPrompt}
+  `);
+        },
+        onMessage:(chat:FmodeChat,message:FmodeChatMessage)=>{
+          console.log("onMessage",message)
+          let content:any = message?.content
+          if(typeof content == "string"){
+            if(content?.indexOf("[处方完成]")>-1){
+              console.log("门诊已完成")
+              consult.set({
+                content:content // 处方内容
+              })
+              consult.save();
+            }
+          }
+        },
+        onChatSaved:(chat:FmodeChat)=>{
+          // chat?.chatSession?.id 本次会话的 chatId
+          console.log("onChatSaved",chat,chat?.chatSession,chat?.chatSession?.id)
+        }
+      }
+      openChatPanelModal(this.modalCtrl,options)
+    }
+  }
+  

+ 3 - 3
mcbridge-app/src/app/tab3/tab3.page.html

@@ -72,17 +72,17 @@
       </ion-card-header>
       <ion-card-content>
         @if(!currentUser?.id){
-          请登录后查看...
+          <P>请登录后查看...</P>
         }
         @if(currentUser?.id){
         <ion-list>
-          <ion-item *ngFor="let expert of expertList" lines="none">
+          <ion-item *ngFor="let expert of expertList" lines="none" class="doctor-item">
             <ion-thumbnail slot="start">
               <img [src]="expert.get('avatar')" [alt]="expert.get('name')" />
             </ion-thumbnail>
             <div class="doctor-info">
               <h3>{{ expert.get('name') }}({{ expert.get('age') }}岁)</h3>
-              <p>{{ expert.get('title') }},{{ expert.get('depart') }}</p>
+              <p>{{ expert.get('title') }} {{ expert.get('specialist') }}</p>
             </div>
           </ion-item>
         </ion-list>

+ 25 - 0
mcbridge-app/src/app/tab3/tab3.page.scss

@@ -59,4 +59,29 @@ ion-toolbar {
   .button-row ion-button:active {
     transform: scale(0.95); /* 按钮缩小 */
     background-color: gray; /* 按下时背景颜色设为灰色 */
+  }
+
+  // 关注列表
+  .doctor-item {
+    display: flex;
+    align-items: center;
+    // padding: 15px; /* 增加内边距 */
+    margin-bottom: 10px; /* 增加每个医生项之间的间距 */
+    border: 1px solid #e0e0e0; /* 添加边框 */
+    border-radius: 8px; /* 圆角边框 */
+    background-color: #f9f9f9; /* 背景颜色 */
+  }
+
+  .doctor-item ion-thumbnail {
+    margin-right: 15px; /* 增加缩略图和文本之间的间距 */
+  }
+
+  .doctor-info h3 {
+    margin: 0; /* 去掉默认的外边距 */
+    font-size: 1.2em; /* 调整字体大小 */
+  }
+
+  .doctor-info p {
+    margin: 5px 0; /* 增加段落之间的间距 */
+    color: #666; /* 调整字体颜色 */
   }

+ 5 - 0
mcbridge-app/src/app/tabs/tabs.routes.ts

@@ -31,6 +31,11 @@ export const routes: Routes = [
         loadComponent: () =>
           import('../inquiry/inquiry.component').then((m) => m.InquiryComponent),
       },
+      {
+        path: 'knowledge-page',
+        loadComponent: () =>
+          import('../knowledge/knowledge-page/knowledge-page.component').then((m) => m.KnowledgePageComponent),
+      },
       {
         path: '',
         redirectTo: '/tabs/tab1',

+ 9 - 0
mcbridge-app/src/app/test-chat-panel/test-chat-panel.component.html

@@ -0,0 +1,9 @@
+<app-chat-panel *ngIf="leftButtons?.length&&modelList?.length" #chatComp 
+[roleId]="roleId" 
+[chatId]="chatId" 
+[leftButtons]="leftButtons" 
+[modelList]="modelList" 
+[isDirect]="isDirect"
+[hideModalSelect]="hideModalSelect"
+[hideInputPreview]="hideInputPreview"
+></app-chat-panel>

+ 3 - 0
mcbridge-app/src/app/test-chat-panel/test-chat-panel.component.scss

@@ -0,0 +1,3 @@
+app-chat-panel {
+    height: 100vh;
+}

+ 22 - 0
mcbridge-app/src/app/test-chat-panel/test-chat-panel.component.spec.ts

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

+ 243 - 0
mcbridge-app/src/app/test-chat-panel/test-chat-panel.component.ts

@@ -0,0 +1,243 @@
+import { CommonModule } from '@angular/common';
+import { ChangeDetectorRef, Component, OnInit, ViewChild } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+import { ModalController } from '@ionic/angular/standalone';
+import { ChatPanelComponent } from 'fmode-ng'
+import Parse from "parse";
+import { combineLatest } from 'rxjs';
+
+// 添加Icons
+import { addIcons } from 'ionicons';
+import * as icons from 'ionicons/icons';
+addIcons(icons);
+
+@Component({
+  selector: 'app-test-chat-panel',
+  templateUrl: './test-chat-panel.component.html',
+  styleUrls: ['./test-chat-panel.component.scss'],
+  standalone: true,
+  imports:[
+    CommonModule,
+    ChatPanelComponent,
+  ]
+})
+export class TestChatPanelComponent  implements OnInit {
+  @ViewChild(ChatPanelComponent) chatComp:ChatPanelComponent|undefined
+  leftButtons:any[]=[]
+  modelList:any[]=[]
+  isDirect:boolean=true;
+  hideShare:boolean=true;
+  hideModalSelect:boolean=true;
+  hideInputPreview:boolean = true;
+  chatId:string = ""
+  roleId:string = ""
+  pid:string = ""
+  constructor(
+    private route:ActivatedRoute,
+    private cdRef:ChangeDetectorRef,
+    private modalCtrl:ModalController
+  ) { 
+    combineLatest([this.route.params,this.route.queryParams]).subscribe(async (data:any)=>{
+      let params = data[0] || {}
+
+      this.chatId = params['chatId'] || this.chatId || null;
+      this.roleId = params['roleId'] || this.roleId || null;
+      this.pid = params['pid'] || this.pid || null;
+      console.log("this.pid",this.pid)
+      // 异步加载的后续数据 操作按钮
+      let bint = setInterval(() => {
+        if(this.roleId){
+          clearInterval(bint);
+          return
+        }
+        this.initPanelConfig();
+      }, 2000);
+    })
+  }
+
+
+  ngOnInit() {
+        this.initPanelConfig();
+        // 异步加载的后续数据 提示词
+        let pint = setInterval(() => {
+          if(this.chatComp?.fmodeChat?.promptList?.length){
+            clearInterval(pint);
+            return
+          }
+          this.getChatPrompt();
+        }, 2000);
+
+        // 异步加载的后续数据 采访人物 ChatSession.person
+        let personInt = setInterval(() => {
+          if(this.chatComp?.fmodeChat?.chatSession?.get("person")){
+            clearInterval(personInt)
+          }
+          if(!this.chatComp?.fmodeChat?.chatSession?.get("person")){
+            if(this.pid){
+              this.chatComp?.fmodeChat?.chatSession?.set("person",{type:"Pointer",className:"Person",objectId:this.pid})
+            }
+          }
+        }, 2000);
+  }
+
+  // 初始化聊天面板的设置
+  initPanelConfig(){
+    this.roleId = this.chatComp?.fmodeChat?.chatSession?.get("role")?.id || this.roleId;
+
+    // 按钮自定义
+     this.leftButtons = [
+       // 提示 当角色配置预设提示词时 显示
+       {
+        title:"话题灵感",
+        showTitle:true,
+        icon:"color-wand-outline",
+        onClick:()=>{
+          if(this.chatComp){
+            this.chatComp.fmodeChat.isPromptModalOpen = true
+          }
+        },
+        show:()=>{
+          return this.chatComp?.fmodeChat?.promptList?.length
+        }
+      }
+   ]
+
+      this.leftButtons.push({ // 总结 结束并归档本次对话
+            title:"AI总结对话",
+            showTitle:true,
+            icon:"archive-outline",
+            onClick:()=>{
+              if(this.chatComp){
+                // this.chatComp.fmodeChat.isPromptModalOpen = true
+                if(this.chatComp.fmodeChat){
+                  console.log(JSON.stringify(this.chatComp.fmodeChat.messageList))
+                  // alert("处理对话记录")
+                }
+              }
+              },
+            show:()=>{ 
+              return !this.chatComp?.fmodeChat?.chatSession?.get("story")?.id
+              }
+        })
+
+        this.leftButtons.push({ // 总结 结束并归档本次对话
+          title:"聊天心理分析",
+          showTitle:true,
+          icon:"archive-outline",
+          onClick:()=>{
+            if(this.chatComp){
+              // this.chatComp.fmodeChat.isPromptModalOpen = true
+              if(this.chatComp.fmodeChat){
+                let messageList = JSON.parse(JSON.stringify(this.chatComp.fmodeChat.messageList))
+                messageList = messageList.filter((item:any)=>item.role!="system"&&item?.hidden!=true)
+                let qaContent = messageList.map((item:any)=>{
+                  let roleName = "当前用户"
+                  if(item.role!="user"){
+                    if(this.chatComp&&this.chatComp.fmodeChat.role){
+                      roleName = this.chatComp.fmodeChat.role.get("name");
+                    }else{
+                      roleName = "AI助理"
+                    }
+                  }
+                  return `${roleName}:${item.content}`
+                }
+                ).join("\n")
+                console.log(qaContent)
+                // alert("处理对话记录")
+              }
+            }
+            },
+          show:()=>{ 
+            return !this.chatComp?.fmodeChat?.chatSession?.get("story")?.id
+            }
+      })
+
+    
+
+      setTimeout(()=>{
+          if(this.chatComp&&this.chatComp.fmodeChat){
+            // 自定义左下角操作按钮
+            console.log("左下角操作按钮",this.chatComp.fmodeChat.leftButtons);
+            this.chatComp.fmodeChat.leftButtons = this.leftButtons;
+            
+            // 自定义角色名称
+            console.log("自定义角色",this.chatComp.fmodeChat.role);
+                        this.chatComp.fmodeChat.role.set("name","晓晓");
+            this.chatComp.fmodeChat.role.set("title","心理咨询师");
+            this.chatComp.fmodeChat.role.set("desc","一名亲切和蔼的心理咨询师,晓晓,年龄36岁");
+            this.chatComp.fmodeChat.role.set("tags",["焦虑","抑郁"]);
+            this.chatComp.fmodeChat.role.set("avatar","https://nova-cloud.obs.cn-south-1.myhuaweicloud.com/storage/aigc/imagine/Q4Zif7fTbK-0.png")
+            this.chatComp.fmodeChat.role.set("prompt",`
+# 角色设定
+您是一名亲切和蔼的心理咨询师,晓晓,年龄36岁,需要完成陪来访者聊聊天,随意轻松一些。
+
+# 对话环节
+0.破冰,互相了解,引导用户介绍自己
+1.拓展话题,根据用户的介绍,拓展一些和其心理状态相关的话题
+- 引导,可深入的点,以用户自述为主
+- 当信息充足时候,确认用户心理状态,并进入下一个环节
+2.引导收尾,委婉引导用户结束本次对话
+- 用户同意结束后,结束本次对话,如果依依不舍,可以再陪聊一会儿`);
+//             this.chatComp.fmodeChat.role.set("name","晓晓");
+//             this.chatComp.fmodeChat.role.set("title","主任医师");
+//             this.chatComp.fmodeChat.role.set("desc","一名专业的全科医生,晓晓,年龄36岁");
+//             this.chatComp.fmodeChat.role.set("tags",["呼吸道","感染科"]);
+//             this.chatComp.fmodeChat.role.set("avatar","https://nova-cloud.obs.cn-south-1.myhuaweicloud.com/storage/aigc/imagine/Q4Zif7fTbK-0.png")
+//             this.chatComp.fmodeChat.role.set("prompt",`
+// # 角色设定
+// 您是一名专业的全科医生,晓晓,年龄36岁,需要完成一次完整的门诊服务。
+
+// # 对话环节
+// 0.导诊(根据用户基本情况,引导挂号合适的科室)
+// 1.预设的问询方式(感冒问呼吸、肚子疼叩诊)
+// - 打招呼,以用户自述为主
+// - 当信息充足时候,确认用户症状对应的科室,并进入下一个环节
+// 2.拓展的问询细节
+// 例如:用户反映呼吸不畅,拓展出:是否咳嗽;是否感觉痛或者痒等其他需要的问题。
+// - 当问询细节补充完成后进入下一个环节
+// 3.初步的诊断结果,并且同时列出检查检验项目
+// 初步诊断:确定需要有哪些进一步检查
+// 检查检验:获取医学客观数据
+// - 等待用户提交客观数据,进入下一阶段
+// 4.给出诊断方案并给出处方
+
+// # 开始话语
+// 当您准备好了,可以以一个医生的身份,向来访的用户打招呼。
+//             `);
+
+            this.cdRef.detectChanges();
+          }
+      },1000)
+    
+
+   // 模型自定义
+   let ChatModel = Parse.Object.extend("ChatModel");
+   let model1 = new ChatModel();
+   model1.set({
+       name:"语伴4.5-128k",
+       code:"fmode-4.5-128k",
+       model:"gpt-4o-mini",
+       credit:0.096,
+   })
+  this.modelList = [model1]
+
+
+   console.log("initPanelConfig",this.leftButtons,this.modelList)
+ }
+
+ async getChatPrompt(){
+     let query = new Parse.Query('ChatPrompt')
+     query.notEqualTo('isDeleted', true)
+    //  query.equalTo('company', localStorage.getItem("company"))
+     query.equalTo('role', this.chatComp?.fmodeChat?.role)
+     query.include('role')
+     let promptData = await query.find()
+     if(this.chatComp&&this.chatComp.fmodeChat){
+       this.chatComp.fmodeChat.promptList = promptData
+       this.chatComp.fmodeChat.promptList.forEach((item:any)=>{
+         let cate = item.get('role').get('promptCates').filter((cate:any) => cate.name == item.get('cate'))
+         item.img = cate[0].img
+        })
+      }
+   }
+}

+ 27 - 68
mcbridge-app/src/app/test-page/test-page.page.html

@@ -1,69 +1,28 @@
-<ion-menu contentId="main-content">
-  <ion-header>
-    <ion-toolbar>
-      <ion-title>Menu Content</ion-title>
-    </ion-toolbar>
-  </ion-header>
-  <ion-content class="ion-padding">This is the menu content.</ion-content>
-</ion-menu>
-<div class="ion-page" id="main-content">
-  <ion-header>
-    <ion-toolbar>
-      <ion-buttons slot="start">
-        <ion-menu-button></ion-menu-button>
-      </ion-buttons>
-      <ion-title>Menu</ion-title>
-    </ion-toolbar>
-  </ion-header>
-  <ion-content class="ion-padding"> Tap the button in the toolbar to open the menu. </ion-content>
-</div>
+<ion-card-header>
+  <ion-card-title>医疗服务</ion-card-title>
+</ion-card-header>
+<ion-card-content>
+  <div class="button-row1">
+    <ion-button expand="full" fill="outline" color="light" routerLink="/tabs/inquiry">
+      <img src="../../assets/image/picture1.jpg" alt="报病查诊" class="button-icon"/>
+    </ion-button>
+    <ion-button expand="full" fill="outline" color="light" routerLink="/appointments">
+      <img src="https://img.tukuppt.com/png_preview/00/06/02/Ci3mfMQItP.jpg!/fw/780" alt="预约记录" class="button-icon" />
+    </ion-button>
+    <ion-button expand="full" fill="outline" color="light" routerLink="/discussions">
+      <img src="https://img.tukuppt.com/png_preview/00/06/02/Ci3mfMQItP.jpg!/fw/780" alt="讨论记录" class="button-icon" />
+    </ion-button>
+  </div>
 
-<!-- <ion-content [fullscreen]="true">
-  <ion-button (click)="doPoemTask()">执行诗文意境绘制任务集</ion-button>
-  <ion-button (click)="doInqueryTask()">执行问诊任务集</ion-button>
-   <ion-button (click)="testJSON()">测试JSON</ion-button>
- 
- 
-  <ul>
-   @for(step of taskList;track step.title;){
-     <div> -->
-       <!-- 待开始 -->
-       <!-- @if(step.progress==0 && !step.error){
-         <ion-icon name="radio-button-off-outline"></ion-icon>
-       } -->
-       <!-- 进行中 -->
-       <!-- @if(step.progress!=0 && step.progress!=1){
-         <ion-icon name="reload-outline"></ion-icon>
-       } -->
-       <!-- 已完成 -->
-       <!-- @if(step.progress==1){
-         <ion-icon name="checkmark-circle-outline"></ion-icon>
-       } -->
-       <!-- 已出错 -->
-       <!-- @if(step.error){
-         <ion-icon name="close-circle-outline"></ion-icon>
-       }
-       {{step.title}}  @if(step.progress){<span>{{step.progress * 100 | number:"2.0-0"}}%</span>}
- 
-       @if(step.error){
-         <span style="color:red;">{{step.error}}</span>
-       }
-     </div>   
-   }
-   </ul> -->
- 
-   <!-- 诗词意境绘画生成结果 -->
-   <!-- @if(shareData.images) {
-     @for(imageUrl of shareData.images;track imageUrl){
-       <img [src]="imageUrl" alt="" srcset="">
-     }
-   } -->
- 
-   <!-- 问诊报告生成结果 -->
-   <!-- @if(shareData.diagResult){
-     <h1>{{shareData.diagResult.title}}</h1>
-     <h2>{{shareData.diagResult.desc}}</h2>
-     <p>{{shareData.diagResult.content}}</p>
-   }
- 
- </ion-content> -->
+  <div class="button-row2">
+    <ion-button expand="full" fill="outline" color="light" routerLink="/tabs/inquiry">
+      <img src="https://img.tukuppt.com/png_preview/00/06/02/Ci3mfMQItP.jpg!/fw/780" alt="报病查诊" class="button-icon"/>
+    </ion-button>
+    <ion-button expand="full" fill="outline" color="light" routerLink="/appointments">
+      <img src="https://img.tukuppt.com/png_preview/00/06/02/Ci3mfMQItP.jpg!/fw/780" alt="预约记录" class="button-icon" />
+    </ion-button>
+    <ion-button expand="full" fill="outline" color="light" routerLink="/discussions">
+      <img src="https://img.tukuppt.com/png_preview/00/06/02/Ci3mfMQItP.jpg!/fw/780" alt="讨论记录" class="button-icon" />
+    </ion-button>
+  </div>
+</ion-card-content>

+ 26 - 54
mcbridge-app/src/app/test-page/test-page.page.scss

@@ -1,62 +1,34 @@
-
-
-.profile-container {
-    padding: 16px;
-  }
-  
-  .user-info {
-    display: flex;
-    align-items: center;
-    margin-bottom: 16px;
-  }
-  
-  .avatar {
-    width: 80px;
-    height: 80px;
-    border-radius: 50%;
-    margin-right: 16px;
-  }
-  
-  .auth-text {
-    text-align: center; /* 中间对齐 */
-    margin-left: 16px; /* 与用户信息之间的间距 */
-  }
-
-  .auth-text span{
-    color: blue;
-  }
-
-  ion-card-title {
-    margin: 0px 0;
-    font-size: 20px;
-  }
-  
-  ion-item {
-    --background: transparent; /* 使按钮背景透明 */
-    --box-shadow: none; /* 去掉卡片阴影 */
-  }
-
-  /* 设置顶部工具栏的背景颜色为白色 */
-ion-toolbar {
-    --background: transparent;
-    --color: black; /* 设置文字颜色为黑色 */
-  }
-
   /* 按钮行样式 */
-.button-row {
+  .button-row1 {
     display: flex;
     justify-content: space-between; /* 按钮均匀分布 */
     flex-wrap: wrap; /* 允许换行 */
   }
   
-  .button-row ion-button {
+  .button-row1 ion-button {
     flex: 1; /* 使按钮均匀分布 */
     margin: 4px; /* 按钮之间的间距 */
-  }
-
-
-  /* 鼠标按下效果 */
-  .button-row ion-button:active {
-    transform: scale(0.95); /* 按钮缩小 */
-    background-color: gray; /* 按下时背景颜色设为灰色 */
-  }
+    flex-basis: 80px; /* 设置按钮的基础宽度 */
+    min-width: 80px; /* 设置按钮的最小宽度 */
+    max-width: 50px; /* 设置按钮的最大宽度 */
+  }
+
+    /* 按钮行样式 */
+.button-row2 {
+  display: flex;
+  justify-content: space-between; /* 按钮均匀分布 */
+  flex-wrap: wrap; /* 允许换行 */
+}
+
+.button-row2 ion-button {
+  flex: 1; /* 使按钮均匀分布 */
+  margin: 4px; /* 按钮之间的间距 */
+  flex-basis: 80px; /* 设置按钮的基础宽度 */
+  min-width: 80px; /* 设置按钮的最小宽度 */
+  max-width: 50px; /* 设置按钮的最大宽度 */
+}
+
+.button-icon {
+  max-width: 100%; /* 确保图片不会超出按钮的宽度 */
+  height: auto; /* 保持图片的纵横比 */
+}

+ 115 - 4
mcbridge-app/src/app/test-page/test-page.page.ts

@@ -5,6 +5,11 @@ import { IonContent, IonHeader, IonTitle, IonToolbar } from '@ionic/angular/stan
 import { IonButtons, IonMenu, IonMenuButton } from '@ionic/angular/standalone';
 import { IonCard } from '@ionic/angular/standalone';
 import { IonCardContent } from '@ionic/angular/standalone';
+import { ModalController } from '@ionic/angular/standalone';
+import { Router } from '@angular/router';
+import { ChatPanelOptions, FmChatModalInput, FmodeChat, FmodeChatMessage, openChatPanelModal } from 'fmode-ng';
+import { IonButton } from '@ionic/angular/standalone';
+import { IonCardHeader, IonCardTitle } from '@ionic/angular/standalone';
 
 @Component({
   selector: 'app-test-page',
@@ -12,14 +17,120 @@ import { IonCardContent } from '@ionic/angular/standalone';
   styleUrls: ['./test-page.page.scss'],
   standalone: true,
   imports: [IonContent, IonHeader, IonTitle, IonToolbar, CommonModule, FormsModule,
-    IonMenu, IonButtons, IonMenuButton, IonCard, IonCardContent
+    IonMenu, IonButtons, IonMenuButton, IonCard, IonCardContent, IonButton, FmChatModalInput,
+    IonCardHeader, IonCardTitle
   ]
 })
-export class TestPagePage implements OnInit {
+export class TestPagePage{
+  constructor(
+    private modalCtrl:ModalController,
+    private router:Router,
+    ) {
 
-  constructor() { }
+  }
+  title:string = "123"
+  /** 示例:问诊ChatPanel面板 */
+  openInquiry(){
+    localStorage.setItem("company","E4KpGvTEto")
+    let options:ChatPanelOptions = {
+      roleId:"2DXJkRsjXK",
+      onChatInit:(chat:FmodeChat)=>{
+        console.log("onChatInit");
+              console.log("预设角色",chat.role);
+              chat.role.set("name","晓晓");
+              chat.role.set("title","全科医生");
+              chat.role.set("desc","一名亲切和蔼的门诊全科主任医生,晓晓,年龄36岁");
+              chat.role.set("tags",["全科","门诊"]);
+              chat.role.set("avatar","https://nova-cloud.obs.cn-south-1.myhuaweicloud.com/storage/aigc/imagine/Q4Zif7fTbK-0.png")
+              chat.role.set("prompt",`
+# 角色设定
+您是一名亲切和蔼的专业的全科医生,晓晓,年龄36岁,需要完成一次完整的门诊服务。
+
+# 对话环节
+0.导诊(根据用户基本情况,引导挂号合适的科室)
+1.预设的问询方式(感冒问呼吸、肚子疼叩诊)
+- 打招呼,以用户自述为主
+- 当信息充足时候,确认用户症状对应的科室,并进入下一个环节
+2.拓展的问询细节
+例如:用户反映呼吸不畅,拓展出:是否咳嗽;是否感觉痛或者痒等其他需要的问题。
+- 当问询细节补充完成后进入下一个环节
+3.初步的诊断结果,并且同时列出检查检验项目
+初步诊断:确定需要有哪些进一步检查
+检查检验:获取医学客观数据
+- 等待用户提交客观数据,进入下一阶段
+4.给出诊断方案并给出处方
+- 完成处方时,请在消息结尾附带: [完成]
+
+# 开始话语
+当您准备好了,可以以一个医生的身份,向来访的用户打招呼。`);
+      },
+      onMessage:(chat:FmodeChat,message:FmodeChatMessage)=>{
+        console.log("onMessage",message)
+        let content:any = message?.content
+        if(typeof content == "string"){
+          if(content?.indexOf("[完成]")>-1){
+            console.log("门诊已完成")
+          }
+        }
+      },
+      onChatSaved:(chat:FmodeChat)=>{
+        // chat?.chatSession?.id 本次会话的 chatId
+        console.log("onChatSaved",chat,chat?.chatSession,chat?.chatSession?.id)
+      }
+    }
+    openChatPanelModal(this.modalCtrl,options)
+  }
+
+  /**
+   * 开始聊天
+   */
+  openChat(){
+    let options:ChatPanelOptions = {
+      roleId:"2DXJkRsjXK",
+      onChatSaved:(chat:FmodeChat)=>{
+        // chat?.chatSession?.id 本次会话的 chatId
+        console.log("onChatSaved",chat,chat?.chatSession,chat?.chatSession?.id)
+      },
+    }
+    openChatPanelModal(this.modalCtrl,options)
+  }
+  /**
+   * 恢复聊天
+   * @chatId 从onChatSaved生命周期中,获取chat?.chatSession?.id
+   */
+  restoreChat(chatId:string){
+    let options:ChatPanelOptions = {
+      roleId:"2DXJkRsjXK",
+      chatId:chatId
+    }
+    openChatPanelModal(this.modalCtrl,options)
+  }
 
-  ngOnInit() {
+  goChat(){
+    this.router.navigateByUrl("/chat/session/role/2DXJkRsjXK")
   }
 
+
+  // audioModalHeightPoint:number = 0.35;
+  // async startTalk(){
+  //   // 根据手机兼容性,适配组件弹出高度
+  //   let height = document.body.clientHeight || 960;
+  //   this.audioModalHeightPoint = Number((165/height).toFixed(2));
+
+  //   // 弹出组件
+  //   let modal:any
+  //   let chat:any
+  //   modal = await this.modalCtrl.create({
+  //     component:ModalAudioMessageComponent,
+  //     componentProps:{
+  //       chat:chat,
+  //       modal:modal,
+  //       onBreakPointSet:()=>{
+  //         modal?.setCurrentBreakpoint(this.audioModalHeightPoint)
+  //       }
+  //     }
+  //   })
+  //   modal.present();
+  // }
+
 }

BIN
mcbridge-app/src/assets/image/picture1.jpg


BIN
mcbridge-app/src/assets/image/tab1.医疗诊断.png


BIN
mcbridge-app/src/assets/image/tab1.相似症状查询.png


BIN
mcbridge-app/src/assets/image/tab13.png


BIN
mcbridge-app/src/assets/image/tab14.png


BIN
mcbridge-app/src/assets/image/tab15.png


BIN
mcbridge-app/src/assets/image/tab16.png


BIN
mcbridge-app/src/assets/image/tab1主页.jpg


BIN
mcbridge-app/src/assets/image/住院.png


BIN
mcbridge-app/src/assets/image/医护云桥tab2.jpg


BIN
mcbridge-app/src/assets/image/屏幕截图 2024-12-21 210934.png


BIN
mcbridge-app/src/assets/image/屏幕截图 2024-12-21 211238.png


BIN
mcbridge-app/src/assets/image/屏幕截图 2024-12-21 211252.png


BIN
mcbridge-app/src/assets/image/屏幕截图 2024-12-21 211316.png


BIN
mcbridge-app/src/assets/image/屏幕截图 2024-12-21 211345.png


BIN
mcbridge-app/src/assets/image/屏幕截图 2024-12-21 211429.png


BIN
mcbridge-app/src/assets/image/急诊.png


BIN
mcbridge-app/src/assets/image/门诊.png


BIN
mcbridge-app/src/assets/image/预防保健.png


+ 1 - 1
mcbridge-app/src/main.ts

@@ -16,7 +16,7 @@ Parse.serverURL = "https://server.fmode.cn/parse";
 localStorage.setItem("NOVA_APIG_SERVER", 'aHR0cHMlM0ElMkYlMkZzZXJ2ZXIuZm1vZGUuY24lMkZhcGklMkZhcGlnJTJG')
 
 // 注意:替换Token 根据Token设置Parse服务帐套权限
-Parse.User.become("r:71c794e9a9c2b65412930a30f5a208cc")
+Parse.User.become("r:34c50ea509eb21c9b307a76667b387df")
 
 bootstrapApplication(AppComponent, {
   providers: [

+ 2 - 1
mcbridge-app/tsconfig.json

@@ -19,7 +19,8 @@
     "target": "es2022",
     "module": "es2020",
     "lib": ["es2018", "dom"],
-    "useDefineForClassFields": false
+    "useDefineForClassFields": false,
+    "allowSyntheticDefaultImports":true,
   },
   "angularCompilerOptions": {
     "enableI18nLegacyMessageIdFormat": false,