惊鸿戏梦 3 月之前
父節點
當前提交
47b632035a
共有 33 個文件被更改,包括 1638 次插入37 次删除
  1. 641 14
      youth-app/package-lock.json
  2. 1 0
      youth-app/package.json
  3. 18 0
      youth-app/src/agent/agent-user-input/agent-user-input.component.html
  4. 0 0
      youth-app/src/agent/agent-user-input/agent-user-input.component.scss
  5. 22 0
      youth-app/src/agent/agent-user-input/agent-user-input.component.spec.ts
  6. 40 0
      youth-app/src/agent/agent-user-input/agent-user-input.component.ts
  7. 19 0
      youth-app/src/agent/agent.input.ts
  8. 11 0
      youth-app/src/agent/agent.start.ts
  9. 65 0
      youth-app/src/agent/agent.task.ts
  10. 35 0
      youth-app/src/agent/tasks/poem/poem-desc.ts
  11. 28 0
      youth-app/src/agent/tasks/poem/poem-picture.ts
  12. 16 0
      youth-app/src/app/app.routes.ts
  13. 20 0
      youth-app/src/app/page-inquiry/page-inquiry.component.html
  14. 0 0
      youth-app/src/app/page-inquiry/page-inquiry.component.scss
  15. 22 0
      youth-app/src/app/page-inquiry/page-inquiry.component.spec.ts
  16. 54 0
      youth-app/src/app/page-inquiry/page-inquiry.component.ts
  17. 33 0
      youth-app/src/app/poem-picture/poem-picture.component.html
  18. 0 0
      youth-app/src/app/poem-picture/poem-picture.component.scss
  19. 22 0
      youth-app/src/app/poem-picture/poem-picture.component.spec.ts
  20. 69 0
      youth-app/src/app/poem-picture/poem-picture.component.ts
  21. 4 0
      youth-app/src/app/tab1/tab1.page.html
  22. 8 0
      youth-app/src/app/tab1/tab1.page.ts
  23. 2 8
      youth-app/src/app/tab2/tab2.page.html
  24. 39 4
      youth-app/src/app/tab2/tab2.page.ts
  25. 32 7
      youth-app/src/app/tab3/tab3.page.html
  26. 127 4
      youth-app/src/app/tab3/tab3.page.ts
  27. 10 0
      youth-app/src/app/tabs/tabs.routes.ts
  28. 9 0
      youth-app/src/app/test-chat-panel/test-chat-panel.component.html
  29. 3 0
      youth-app/src/app/test-chat-panel/test-chat-panel.component.scss
  30. 22 0
      youth-app/src/app/test-chat-panel/test-chat-panel.component.spec.ts
  31. 243 0
      youth-app/src/app/test-chat-panel/test-chat-panel.component.ts
  32. 22 0
      youth-app/src/main.ts
  33. 1 0
      youth-app/tsconfig.json

File diff suppressed because it is too large
+ 641 - 14
youth-app/package-lock.json


+ 1 - 0
youth-app/package.json

@@ -27,6 +27,7 @@
     "@capacitor/keyboard": "6.0.3",
     "@capacitor/status-bar": "6.0.2",
     "@ionic/angular": "^8.0.0",
+    "fmode-ng": "^0.0.62",
     "ionicons": "^7.2.1",
     "rxjs": "~7.8.0",
     "tslib": "^2.3.0",

+ 18 - 0
youth-app/src/agent/agent-user-input/agent-user-input.component.html

@@ -0,0 +1,18 @@
+<ion-header>
+  <ion-toolbar>
+    <ion-buttons slot="start">
+      <ion-button color="medium" (click)="cancel()">取消</ion-button>
+    </ion-buttons>
+    <ion-title>用户输入</ion-title>
+    <ion-buttons slot="end">
+      <ion-button (click)="confirm()" [strong]="true">确认</ion-button>
+    </ion-buttons>
+  </ion-toolbar>
+</ion-header>
+<ion-content class="ion-padding">
+  @for(filed of filedsArray;track filed.name){
+    <ion-item>
+      <ion-input labelPlacement="stacked" label="请输入:{{filed.name}}" [(ngModel)]="inputData['poem']" placeholder="{{filed.desc}}"></ion-input>
+    </ion-item>
+  }
+</ion-content>

+ 0 - 0
youth-app/src/agent/agent-user-input/agent-user-input.component.scss


+ 22 - 0
youth-app/src/agent/agent-user-input/agent-user-input.component.spec.ts

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

+ 40 - 0
youth-app/src/agent/agent-user-input/agent-user-input.component.ts

@@ -0,0 +1,40 @@
+import { Component, Input, OnInit } from '@angular/core';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { IonButton, IonButtons, IonContent, IonHeader, IonInput, IonItem, IonTitle, IonToolbar, ModalController } from '@ionic/angular/standalone';
+
+export interface AgentUserInputFiled{
+  name:string//字段名称
+  type:string//输入类型
+  desc:string//字段说明
+}
+@Component({
+  selector: 'agent-user-input',
+  templateUrl: './agent-user-input.component.html',
+  styleUrls: ['./agent-user-input.component.scss'],
+  imports:[
+    IonHeader,IonContent,IonInput,IonItem,IonButton,IonToolbar,IonButtons,
+    FormsModule,ReactiveFormsModule,IonTitle
+  ],
+  standalone: true
+})
+export class AgentUserInputComponent  implements OnInit {
+  poem:string="";
+  
+  @Input()
+  filedsArray:AgentUserInputFiled[]=[]
+
+  @Input()
+  inputData:any ={}
+
+  constructor(private modalCtrl: ModalController) {}
+  ngOnInit() { 
+    
+  }
+  cancel() {
+    return this.modalCtrl.dismiss(null, 'cancel');
+  }
+
+  confirm() {
+    return this.modalCtrl.dismiss(this.inputData, 'confirm');
+  }
+}

+ 19 - 0
youth-app/src/agent/agent.input.ts

@@ -0,0 +1,19 @@
+import { ModalController } from '@ionic/angular/standalone';
+import { AgentUserInputComponent } from 'src/agent/agent-user-input/agent-user-input.component';
+
+  // 数据搜集
+  //@description
+  //https://ionicframework.com/docs/api/modal#controller-modals
+  export async function getUserInput(modalCtrl:ModalController,options:{filedsArray:any}){
+    const modal = await modalCtrl.create({
+      component: AgentUserInputComponent,
+      componentProps:{filedsArray:options.filedsArray}
+    });
+    modal.present();
+
+    const { data, role } = await modal.onWillDismiss();
+
+    if (role === 'confirm') {
+      return data;
+    }
+  }

+ 11 - 0
youth-app/src/agent/agent.start.ts

@@ -0,0 +1,11 @@
+import { AgentTaskStep } from "./agent.task";
+
+  //任务执行函数
+  export async function startTask( taskStepList:AgentTaskStep[]){
+    for(let index=0;index<taskStepList.length;index++){
+      let step=taskStepList[index];
+      let result=await step.handle();
+      if(result == false){break;}
+      if(!step.error){}//任务正常执行,无error
+      }
+    }

+ 65 - 0
youth-app/src/agent/agent.task.ts

@@ -0,0 +1,65 @@
+//任务步骤初始化参数
+interface AgentTaskStepOptions{
+    title:string
+    shareData?:any
+    handle?():{():Promise<any>}
+}
+export class AgentTaskStep{
+    //任务名称
+    title:string="";
+
+    //任务数据 共享数据(整个任务集共享传递的数据)
+    shareData:any={};
+
+    //任务数据 私有数据(当前任务私有的处理数据)
+    data:any={};
+
+    //父级组件:任务集组件
+    //@example当前子任务未完成,且当前任务是必须时,需要在当前任务中,终止整个程序
+    parentComp:any|undefined
+    //当前任务在任务集数组中的项目
+    parentIndex:number|undefined
+    
+    /*任务生命周期
+    * @constructor  //构造时执行一次
+    * @onInit  //初始化后执行一次
+    * @onProgress // 进度变化时执行一次
+    * @onComplete // 完成前执行一次
+    */
+    constructor(metaData:AgentTaskStepOptions){
+        this.title=metaData.title
+        this.shareData=metaData.shareData
+        if(metaData.handle){
+            this.handle=metaData.handle;
+        }
+        this.init(metaData)
+    }
+
+    OnInit(step:AgentTaskStep){
+        this.isInit=true;
+    };
+    beforeHandle(step:AgentTaskStep){};
+    afterHandle(step:AgentTaskStep){};
+    onProgress(step:AgentTaskStep){};
+    onComplete(step:AgentTaskStep){};
+    onError(step:AgentTaskStep){};
+    //任务初始化函数
+     async init(metaData:AgentTaskStepOptions){
+
+    }
+    isInit:boolean=false;//是否初始化
+
+    //任务执行函数
+    //@desc 函数内必须有明确的完成、结束情形,函数内必须有进度的变化指示 从0.00 - 1.00
+    progress:number=0;
+    handle():Promise<any>|any{
+        
+        this.beforeHandle(this)
+
+        this.onComplete(this)
+        this.onError(this)
+        return true
+    }
+    error: string="";//错误原因
+
+}

+ 35 - 0
youth-app/src/agent/tasks/poem/poem-desc.ts

@@ -0,0 +1,35 @@
+import { ModalController } from "@ionic/angular/standalone";
+import { FmodeChatCompletion } from "fmode-ng";
+import { getUserInput } from "src/agent/agent.input";
+import { AgentTaskStep } from "src/agent/agent.task";
+
+export function TaskPoemPicutreDesc(options:{modalCtrl:ModalController,shareData:any}){
+    let task1=new AgentTaskStep({title:"意境分析",shareData:options.shareData});
+    task1.handle=async()=>{
+      //获取用户输入的诗词
+      let userInput=await getUserInput(options.modalCtrl,{filedsArray:[{name:"诗文内容",type:"text",desc:"诗文内容"}]});
+      console.log("已获取用户输入:",userInput)
+      console.log("已获取诗文内容:",userInput.poem)
+
+    // 文本生成
+    let PromptTemplate = `您是一名专业的美术画家,请您根据古诗文的内容,将其描述的画面、场景、人物、物品等用最简短的语言表达,直接写出画面,并且以中国的古风意境为主
+    诗文如下:${userInput.poem}`
+    let completion = new FmodeChatCompletion([
+      {role:"system",content:""},
+      {role:"user",content:PromptTemplate}
+    ])
+    completion.sendCompletion().subscribe((message:any)=>{
+      // 打印消息体
+      console.log(message.content)
+      // 赋值消息内容给组件内属性
+      options.shareData.PictureDescResult = message.content
+      if(message?.complete){ // 判断message为完成状态,则设置isComplete为完成
+        task1.progress=1
+      }
+    })
+      // await this.wait(1000);
+      // console.log("意境分析:执行过程");
+      // task1.progress=1
+      return task1;
+    }
+}

+ 28 - 0
youth-app/src/agent/tasks/poem/poem-picture.ts

@@ -0,0 +1,28 @@
+
+import { ModalController } from "@ionic/angular/standalone";
+import { DalleOptions, ImagineWork } from "fmode-ng";
+import { AgentTaskStep } from "src/agent/agent.task";
+
+// export function TaskPoemPictureCreate(options:{modalCtrl:ModalController,shareData:any}){
+//     let task2=new AgentTaskStep({title:"意境绘制",shareData:options.shareData});
+//     task2.handle=async()=>{
+//       //await this.wait(10000);
+//       //let userInput=await getUserInput(this.modalCtrl,{filedsArray:[{name:"绘图要求",type:"text",desc:"画风、构图等"}]});
+//       // console.log("已获取用户输入:",userInput)
+//       // console.log("已获取诗文内容:",userInput.poem)
+//       console.log("意境绘制:执行过程");
+//       let imagineWork = new ImagineWork();
+
+//       // 图片生成
+//       let PicturePrompt = `${options.shareData.PictureDescResult}\n风格:中国古风。画面不带任何文字。`
+//       let options:DalleOptions = {prompt:PicturePrompt}
+//       imagineWork?.draw(options).subscribe((work:any)=>{
+//           console.log("imagineWork",work?.toJSON())
+//           console.log("images",work?.get("images"))
+//           if(work?.get("images")?.length){
+//             options.shareData.images = work?.get("images");
+//             task2.progress=1
+//           }
+//       })
+//     }
+// }

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

@@ -5,4 +5,20 @@ export const routes: Routes = [
     path: '',
     loadChildren: () => import('./tabs/tabs.routes').then((m) => m.routes),
   },
+  // 聊天模块
+  {
+    path: "chat/session/role/:roleId",
+    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'
+    },
 ];

+ 20 - 0
youth-app/src/app/page-inquiry/page-inquiry.component.html

@@ -0,0 +1,20 @@
+<ion-content>
+  <h2>科室</h2>
+  <ion-input [value]="keshi" (ionInput)="keshiInput($event)"></ion-input>
+  <!-- 文本域:生成提示词 -->
+  <h2>症状的描述</h2>
+  <ion-textarea [value]="userPrompt" (ionInput)="promptInput($event)" placeholder="文本提示词" autoGrow="true"></ion-textarea>
+
+  <!-- 按钮:执行消息生成函数 -->
+  <ion-button (click)="sendMessage()" expand="block">初步诊断</ion-button>
+    
+  <!-- 展示:返回消息内容 -->
+   <!-- 消息传输过程中,实时预览 -->
+   @if(!isComplete){
+    <div>{{responseMsg}}</div>
+  }
+   <!-- 消息传输完成后,实时预览Markdown格式 -->
+  @if(isComplete){
+    <fm-markdown-preview class="content-style" [content]="responseMsg"></fm-markdown-preview>
+  }
+</ion-content>

+ 0 - 0
youth-app/src/app/page-inquiry/page-inquiry.component.scss


+ 22 - 0
youth-app/src/app/page-inquiry/page-inquiry.component.spec.ts

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

+ 54 - 0
youth-app/src/app/page-inquiry/page-inquiry.component.ts

@@ -0,0 +1,54 @@
+import { Component, OnInit } from '@angular/core';
+import { IonButton, IonContent, IonHeader, IonInput, IonTextarea, IonTitle, IonToolbar } from '@ionic/angular/standalone';
+import { FmodeChatCompletion,MarkdownPreviewModule } from 'fmode-ng';
+
+@Component({
+  selector: 'app-page-inquiry',
+  templateUrl: './page-inquiry.component.html',
+  styleUrls: ['./page-inquiry.component.scss'],
+  imports: [IonHeader, IonToolbar, IonTitle, IonContent, IonButton,
+    IonTextarea,IonInput,MarkdownPreviewModule
+    ],
+  standalone: true,
+})
+export class PageInquiryComponent  implements OnInit {
+
+  ngOnInit() {}
+
+  constructor() {}
+
+  // 用户输入提示词
+
+  keshi:string = "门诊"
+  keshiInput(ev:any){
+    this.keshi = ev.detail.value;
+  }
+   userPrompt:string = "请描述您的症状"
+  promptInput(ev:any){
+    this.userPrompt = ev.detail.value;
+  }
+  // 属性:组件内用于展示消息内容的变量
+  responseMsg:any = ""
+  // 方法:实例化completion对象,传入消息数组,并订阅生成的可观察对象。
+  isComplete:boolean=false;// 定义完成状态属性,用来标记是否补全完成
+  sendMessage(){
+    console.log("create")
+
+    let PromptTemplate =`您作为一名专业的${this.keshi}医生,请您根据用户描述的症状,给出初步的诊断,并给出一些建议。以下是用户口述:${this.userPrompt}`
+
+    let completion = new FmodeChatCompletion([
+      {role:"system",content:""},
+      {role:"user",content:PromptTemplate}
+    ])
+    completion.sendCompletion().subscribe((message:any)=>{
+      // 打印消息体
+      console.log(message.content)
+      // 赋值消息内容给组件内属性
+      this.responseMsg = message.content
+      if(message?.complete){// 判断message为完成状态,则设置isComplete为完成
+        this.isComplete=true;
+      }
+    })
+  }
+
+}

+ 33 - 0
youth-app/src/app/poem-picture/poem-picture.component.html

@@ -0,0 +1,33 @@
+<ion-header [translucent]="true">
+  <ion-toolbar>
+    <ion-title>
+      示例:古诗文意境生成{{imagineWork?.progress || 0}}
+    </ion-title> 
+  </ion-toolbar>
+</ion-header>
+
+<ion-content [fullscreen]="true">
+  <!-- 生成提示词 -->
+  <ion-textarea [value]="userPrompt" (ionInput)="promptInput($event)" placeholder="古诗文填写" autoGrow="true"></ion-textarea>
+  <ion-button (click)="createImage()" expand="block">生成意境</ion-button>
+  <!-- 意境画面提示词 -->
+  <div>
+    {{PictureDescResult}}
+  </div>
+  <!-- 生成结果 -->
+  @if(images.length) {
+    @for(imageUrl of images;track imageUrl){
+      <img [src]="imageUrl" alt="" srcset="">
+    }
+  }
+  <!-- 生成状态 -->
+  @if(!images.length){
+    @if(imagineWork){
+      <h1>生成中</h1>
+    }
+    @if(!imagineWork){
+      <h1>未开始</h1>
+    }
+  }
+
+</ion-content>

+ 0 - 0
youth-app/src/app/poem-picture/poem-picture.component.scss


+ 22 - 0
youth-app/src/app/poem-picture/poem-picture.component.spec.ts

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

+ 69 - 0
youth-app/src/app/poem-picture/poem-picture.component.ts

@@ -0,0 +1,69 @@
+import { Component, OnInit } from '@angular/core';
+import { IonHeader, IonToolbar, IonTitle, IonContent } from '@ionic/angular/standalone';
+import { IonTextarea, IonButton,IonInput } from "@ionic/angular/standalone";
+import { DalleOptions, ImagineWork, FmodeChatCompletion } from 'fmode-ng';
+
+@Component({
+  selector: 'app-poem-picture',
+  templateUrl: './poem-picture.component.html',
+  styleUrls: ['./poem-picture.component.scss'],
+  standalone: true,
+  imports: [
+    IonHeader, IonToolbar, IonTitle, IonContent, 
+    IonButton,
+    IonInput,
+    IonTextarea
+  ],
+})
+export class PoemPictureComponent  implements OnInit {
+  userPrompt:string = "犬吠水声中,桃花带露浓。\n树深时见鹿,溪午不闻钟。"
+  promptInput(ev:any){
+    this.userPrompt = ev.detail.value;
+  }
+  imagineWork:ImagineWork|undefined
+  images:Array<string> = []
+  constructor(
+  ){
+    // 示例任务,自己生成图片后请存储新的ID
+    this.imagineWork = new ImagineWork("4mPR0IW1i5");
+    this.imagineWork.fetchTask().then(work=>{
+      this.images = this.imagineWork?.images || [];
+    })
+  }
+
+  PictureDescResult:string = `` // 画面描述结果
+  async createImage(){
+    this.imagineWork = new ImagineWork();
+    // 文本生成
+    let PromptTemplate = `您是一名专业的美术画家,请您根据古诗文的内容,将其描述的画面、场景、人物、物品等用最简短的语言表达,直接写出画面,并且以中国的古风意境为主
+    诗文如下:
+    ${this.userPrompt}
+    `
+    let completion = new FmodeChatCompletion([
+      {role:"system",content:""},
+      {role:"user",content:PromptTemplate}
+    ])
+    completion.sendCompletion().subscribe((message:any)=>{
+      // 打印消息体
+      console.log(message.content)
+      // 赋值消息内容给组件内属性
+      this.PictureDescResult = message.content
+      if(message?.complete){ // 判断message为完成状态,则设置isComplete为完成
+        // 图片生成
+        let PicturePrompt = `${this.PictureDescResult}\n风格:中国古风。画面不带任何文字。突出色彩。`
+        let options:DalleOptions = {prompt:PicturePrompt}
+        this.imagineWork?.draw(options).subscribe(work=>{
+            console.log("imagineWork",work?.toJSON())
+            console.log("images",work?.get("images"))
+            if(work?.get("images")?.length){
+              this.images = work?.get("images");
+            }
+        })
+      }
+    })
+
+    
+  }
+  ngOnInit() {}
+
+}

+ 4 - 0
youth-app/src/app/tab1/tab1.page.html

@@ -19,4 +19,8 @@
   <ion-button (click)="goTestPage()">进入测试页</ion-button>
   <!-- 页面跳转加标签编辑 -->
   <ion-button (click)="navigeteTo()">梦境分析</ion-button>
+
+  <ion-button (click)="goInquiryPage()">AI问诊</ion-button>
+
+  <ion-button (click)="goPicturePage()">AI画图</ion-button>
 </ion-content>

+ 8 - 0
youth-app/src/app/tab1/tab1.page.ts

@@ -21,4 +21,12 @@ export class Tab1Page {
   navigeteTo(){
     this.router.navigate(['/tabs/dream-analysis'])
   }
+
+  goInquiryPage(){
+    this.router.navigate(['/tabs/inquiry']);
+  }
+
+  goPicturePage(){
+    this.router.navigate(['/tabs/picture']);
+  }
 }

+ 2 - 8
youth-app/src/app/tab2/tab2.page.html

@@ -1,17 +1,11 @@
 <ion-header [translucent]="true">
   <ion-toolbar>
     <ion-title>
-      Tab 2
+      心之所需(Chat模块组件演示)
     </ion-title>
   </ion-toolbar>
 </ion-header>
 
 <ion-content [fullscreen]="true">
-  <ion-header collapse="condense">
-    <ion-toolbar>
-      <ion-title size="large">Tab 2</ion-title>
-    </ion-toolbar>
-  </ion-header>
-
-  <app-explore-container name="Tab 2 page"></app-explore-container>
+  <ion-button (click)="goChat()">开始聊天</ion-button>
 </ion-content>

+ 39 - 4
youth-app/src/app/tab2/tab2.page.ts

@@ -1,16 +1,51 @@
 import { Component } from '@angular/core';
-import { IonHeader, IonToolbar, IonTitle, IonContent } from '@ionic/angular/standalone';
+import { IonHeader, IonToolbar, IonTitle, IonContent, IonButton, ModalController } from '@ionic/angular/standalone';
 import { ExploreContainerComponent } from '../explore-container/explore-container.component';
+import { FmChatModalInput } from 'fmode-ng';
+import { Router } from '@angular/router';
+//import { ModalAudioMessageComponent } from 'fmode-ng/lib/aigc/chat/chat-modal-input/modal-audio-message/modal-audio-message.component';
 
 @Component({
   selector: 'app-tab2',
   templateUrl: 'tab2.page.html',
   styleUrls: ['tab2.page.scss'],
   standalone: true,
-  imports: [IonHeader, IonToolbar, IonTitle, IonContent, ExploreContainerComponent]
+  imports: [IonHeader, IonToolbar, IonTitle, IonContent, ExploreContainerComponent,
+    IonButton,
+    // ASR语音输入模块
+    FmChatModalInput,
+    //ModalAudioMessageComponent
+  ]
 })
 export class Tab2Page {
 
-  constructor() {}
-
+  constructor( private modalCtrl:ModalController,
+    private router:Router,) {}
+    
+    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();
+    // }
+  
 }
+

+ 32 - 7
youth-app/src/app/tab3/tab3.page.html

@@ -1,17 +1,42 @@
 <ion-header [translucent]="true">
   <ion-toolbar>
     <ion-title>
-      Tab 3
+      计划健康计划(任务集执行展示)
     </ion-title>
   </ion-toolbar>
 </ion-header>
 
 <ion-content [fullscreen]="true">
-  <ion-header collapse="condense">
-    <ion-toolbar>
-      <ion-title size="large">Tab 3</ion-title>
-    </ion-toolbar>
-  </ion-header>
+  <ion-button (click)="doPoemTask()">执行诗文意境绘制任务集</ion-button>
+  <ion-button (click)="doInqueryTask()">执行问诊任务集</ion-button>
+  <!-- <ion-button (click)="testJSON()">测试JSON</ion-button> -->
 
-  <app-explore-container name="Tab 3 page"></app-explore-container>
+   <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}}{{step.progress}}</div>   
+    }
+    </ul>
+    <!-- 图片结果 -->
+    @if(shareData.images) {
+      @for(imageUrl of shareData.images;track imageUrl){
+        <img [src]="imageUrl" alt="" srcset="">
+      }
+    }
 </ion-content>

+ 127 - 4
youth-app/src/app/tab3/tab3.page.ts

@@ -1,14 +1,137 @@
 import { Component } from '@angular/core';
-import { IonHeader, IonToolbar, IonTitle, IonContent } from '@ionic/angular/standalone';
+import { IonHeader, IonToolbar, IonTitle, IonContent,IonButton,IonIcon, ModalController, } from '@ionic/angular/standalone';
 import { ExploreContainerComponent } from '../explore-container/explore-container.component';
-
+import { AgentTaskStep } from 'src/agent/agent.task';
+import { closeCircleOutline,radioButtonOffOutline,reloadOutline,checkmarkCircleOutline} from 'ionicons/icons';
+import { addIcons } from 'ionicons';
+import { DalleOptions, FmodeChatCompletion, ImagineWork } from 'fmode-ng';
+import { getUserInput } from 'src/agent/agent.input';
+import { startTask } from 'src/agent/agent.start';
+addIcons({radioButtonOffOutline,reloadOutline,checkmarkCircleOutline,closeCircleOutline})
 @Component({
   selector: 'app-tab3',
   templateUrl: 'tab3.page.html',
   styleUrls: ['tab3.page.scss'],
   standalone: true,
-  imports: [IonHeader, IonToolbar, IonTitle, IonContent, ExploreContainerComponent],
+  imports: [IonHeader, IonToolbar, IonTitle, IonContent, ExploreContainerComponent,IonButton,IonIcon],
 })
 export class Tab3Page {
-  constructor() {}
+  constructor(private modalCtrl:ModalController) {
+
+
+  }
+
+  taskList:AgentTaskStep[] = []
+
+  wait(duration:number=1000){
+    return new Promise(resolve=>{
+      setTimeout(() => {
+          resolve(true)
+      }, duration);
+    })
+  }
+
+  shareData:any = {}
+
+  // 任务:完成故事意境描述及图像绘制
+  doPoemTask(){
+    let task1 = new AgentTaskStep({title:"意境分析",shareData:this.shareData});
+    task1.handle=async()=>{
+      //获取用户输入的诗词
+      let userInput=await getUserInput(this.modalCtrl,{filedsArray:[{name:"诗文内容",type:"text",desc:"诗文内容"}]});
+      console.log("已获取用户输入:",userInput)
+      console.log("已获取诗文内容:",userInput.poem)
+
+    // 文本生成
+    let PromptTemplate = `您是一名专业的美术画家,请您根据古诗文的内容,将其描述的画面、场景、人物、物品等用最简短的语言表达,直接写出画面,并且以中国的古风意境为主
+    诗文如下:${userInput.poem}`
+    let completion = new FmodeChatCompletion([
+      {role:"system",content:""},
+      {role:"user",content:PromptTemplate}
+    ])
+    completion.sendCompletion().subscribe((message:any)=>{
+      // 打印消息体
+      console.log(message.content)
+      // 赋值消息内容给组件内属性
+      this.shareData.PictureDescResult = message.content
+      if(message?.complete){ // 判断message为完成状态,则设置isComplete为完成
+        task1.progress=1
+      }
+    })
+      // await this.wait(1000);
+      // console.log("意境分析")
+  }
+
+    let task2=new AgentTaskStep({title:"意境绘制",shareData:this.shareData});
+    task2.handle=async()=>{
+      await this.wait(10000);
+      //let userInput=await getUserInput(this.modalCtrl,{filedsArray:[{name:"绘图要求",type:"text",desc:"画风、构图等"}]});
+      // console.log("已获取用户输入:",userInput)
+      // console.log("已获取诗文内容:",userInput.poem)
+      console.log("意境绘制:执行过程");
+      let imagineWork = new ImagineWork();
+
+      // 图片生成
+      let PicturePrompt = `${this.shareData.PictureDescResult}\n风格:中国古风。画面不带任何文字。`
+      let options:DalleOptions = {prompt:PicturePrompt}
+      imagineWork?.draw(options).subscribe((work:any)=>{
+          console.log("imagineWork",work?.toJSON())
+          console.log("images",work?.get("images"))
+          if(work?.get("images")?.length){
+            this.shareData.images = work?.get("images");
+            task2.progress=1
+          }
+      })
+    }
+    
+    // 定义任务集
+    let PoemTaskList = [task1,task2]
+    // 传递给显示组件
+    this.taskList = PoemTaskList
+    // 开始执行任务
+    startTask(PoemTaskList)
+  }
+
+  // 任务集:医疗问诊任务集 完成一次完整的门诊服务
+  doInqueryTask(){
+    let task1 = new AgentTaskStep({title:"导诊",shareData:this.shareData});
+    task1.handle=async()=>{
+      await this.wait(1000)
+      console.log("导诊:执行过程")
+      task1.progress=1
+    }
+    let task2 = new AgentTaskStep({title:"问诊",shareData:this.shareData});
+    task2.handle=async()=>{
+      await this.wait(1000)
+      console.log("问诊:执行过程")
+      task2.progress=1
+    }
+    let task3 = new AgentTaskStep({title:"初诊",shareData:this.shareData});
+    task3.handle=async()=>{
+      await this.wait(1000)
+      console.log("初诊:执行过程")
+      task3.progress=1
+    }
+    let task4 = new AgentTaskStep({title:"检验",shareData:this.shareData});
+    task4.handle=async()=>{
+      await this.wait(1000)
+      console.log("检验:执行过程")
+      task4.progress=1
+    }
+    let task5 = new AgentTaskStep({title:"处方",shareData:this.shareData});
+    task5.handle=async()=>{
+      await this.wait(1000)
+      console.log("处方:执行过程")
+      task5.progress=1
+    }
+    // 定义任务集
+    let InquireServiceTaskList = [
+      task1,task2,task3,task4,task5
+    ]
+    // 传递给显示组件
+    this.taskList = InquireServiceTaskList
+    // 开始执行任务
+    startTask(InquireServiceTaskList)
+  }
+  
 }

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

@@ -36,6 +36,16 @@ export const routes: Routes = [
         loadComponent: () =>
           import('../page-dream-analysis/page-dream-analysis.component').then((m) => m.PageDreamAnalysisComponent),
       },
+      {
+        path: 'inquiry',
+        loadComponent: () =>
+          import('../page-inquiry/page-inquiry.component').then((m) => m.PageInquiryComponent),
+      },
+      {
+        path: 'picture',
+        loadComponent: () =>
+          import('../poem-picture/poem-picture.component').then((m) => m.PoemPictureComponent),
+      },
       {
         path: '',
         redirectTo: '/tabs/tab1',

+ 9 - 0
youth-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
youth-app/src/app/test-chat-panel/test-chat-panel.component.scss

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

+ 22 - 0
youth-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
youth-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
+        })
+      }
+   }
+}

+ 22 - 0
youth-app/src/main.ts

@@ -5,10 +5,32 @@ import { IonicRouteStrategy, provideIonicAngular } from '@ionic/angular/standalo
 import { routes } from './app/app.routes';
 import { AppComponent } from './app/app.component';
 
+
+// 引用HttpClient方法
+import { provideHttpClient } from '@angular/common/http';
+// 引用移动端授权检测供应器
+import { Diagnostic } from '@awesome-cordova-plugins/diagnostic/ngx';
+// 设置Parse服务属性
+import Parse from "parse";
+Parse.initialize("ncloudmaster");
+Parse.serverURL = "https://server.fmode.cn/parse";
+localStorage.setItem("NOVA_APIG_SERVER", 'aHR0cHMlM0ElMkYlMkZzZXJ2ZXIuZm1vZGUuY24lMkZhcGklMkZhcGlnJTJG')
+
+// 注意:替换Token 根据Token设置Parse服务帐套权限
+Parse.User.become('r:E4KpGvTEto-136270658251731988509')
+
+
 bootstrapApplication(AppComponent, {
   providers: [
     { provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
     provideIonicAngular(),
     provideRouter(routes, withPreloading(PreloadAllModules)),
+     // ...
+    // 添加HttpClient供应器
+    provideHttpClient(),
+    // 添加Diagnostic
+    Diagnostic,
+    { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }
+    // ...
   ],
 });

+ 1 - 0
youth-app/tsconfig.json

@@ -4,6 +4,7 @@
   "compilerOptions": {
     "baseUrl": "./",
     "outDir": "./dist/out-tsc",
+    "allowSyntheticDefaultImports": true,
     "forceConsistentCasingInFileNames": true,
     "strict": true,
     "noImplicitOverride": true,

Some files were not shown because too many files changed in this diff