lzy 1 жил өмнө
parent
commit
630d0dab61

+ 4 - 4
FilmDraw-app/package-lock.json

@@ -22,7 +22,7 @@
         "@capacitor/keyboard": "6.0.3",
         "@capacitor/status-bar": "6.0.2",
         "@ionic/angular": "^8.0.0",
-        "fmode-ng": "^0.0.62",
+        "fmode-ng": "^0.0.63",
         "ionicons": "^7.2.1",
         "rxjs": "~7.8.0",
         "tslib": "^2.3.0",
@@ -10378,9 +10378,9 @@
       "license": "ISC"
     },
     "node_modules/fmode-ng": {
-      "version": "0.0.62",
-      "resolved": "https://registry.npmmirror.com/fmode-ng/-/fmode-ng-0.0.62.tgz",
-      "integrity": "sha512-F0RzEu47NgKpaHp/vBEzjsU4efJ1lKLAbbdPE5hltj1W1cDaeht/i6UlEidid4FAEdAg7c9rrQrLgOh/zUfCsg==",
+      "version": "0.0.63",
+      "resolved": "https://registry.npmmirror.com/fmode-ng/-/fmode-ng-0.0.63.tgz",
+      "integrity": "sha512-gTiDZO2CchcTYAmlaweapasqV/8PdhG2vizJNn5dYZyXjgtrjyW+KeW5k2EVyIDvM1+bMGjjhGmr76Fc0TElxw==",
       "license": "COPYRIGHT © 未来飞马 未来全栈 www.fmode.cn All RIGHTS RESERVED",
       "dependencies": {
         "tslib": "^2.3.0"

+ 1 - 1
FilmDraw-app/package.json

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

+ 4 - 0
FilmDraw-app/src/app/app.routes.ts

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

+ 52 - 0
FilmDraw-app/src/app/interact/interact.page.html

@@ -0,0 +1,52 @@
+<ion-header [translucent]="true">
+  <ion-toolbar>
+    <ion-title size="large">角色互动</ion-title>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content [fullscreen]="true">
+
+    <!-- 角色选择区 -->
+    <section>
+      <ion-card>
+        <ion-card-header>
+          <ion-card-title>选择角色</ion-card-title>
+        </ion-card-header>
+        <ion-card-content>
+          <ion-list>
+            <ion-item *ngFor="let character of characters" (click)="selectCharacter(character)">
+              <ion-avatar slot="start">
+                <img [src]="character.avatar" alt="{{ character.name }} 头像">
+              </ion-avatar>
+              <ion-label>
+                <h2>{{ character.name }}</h2>
+                <p>{{ character.description }}</p>
+              </ion-label>
+            </ion-item>
+          </ion-list>
+        </ion-card-content>
+      </ion-card>
+    </section>
+  
+    <!-- 角色对话区 -->
+    <section *ngIf="selectedCharacter">
+      <ion-card>
+        <ion-card-header>
+          <ion-card-title>与 {{ selectedCharacter.name }} 对话</ion-card-title>
+        </ion-card-header>
+        <ion-card-content>
+          <div class="chat-area">
+            <div *ngFor="let message of chatMessages" class="message">
+              <strong>{{ message.sender }}:</strong> {{ message.text }}
+            </div>
+          </div>
+          <ion-item>
+            <ion-input [(ngModel)]="userMessage" placeholder="输入您的消息..."></ion-input>
+            <ion-button (click)="sendMessage()" slot="end">
+              <ion-icon name="send"></ion-icon>
+            </ion-button>
+          </ion-item>
+        </ion-card-content>
+      </ion-card>
+    </section>
+</ion-content>

+ 50 - 0
FilmDraw-app/src/app/interact/interact.page.scss

@@ -0,0 +1,50 @@
+ion-title {
+    flex: 1; // 使标题占据可用空间
+    text-align: left; // 确保文字左对齐
+    margin-left: 16px; // 左侧边距,可以根据需要调整
+    margin-top: 5px;
+  }
+
+ion-header {
+    background-color: #3880ff; // 设置头部背景色
+  }
+  
+  ion-card {
+    margin: 10px; // 设置卡片之间的间距
+    border-radius: 10px; // 设置卡片圆角
+    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); // 添加阴影效果
+  }
+  
+  ion-card-header {
+    background-color: #f0f0f0; // 设置卡片头部背景色
+  }
+  
+  ion-card-title {
+    font-size: 1.2em; // 设置卡片标题字体大小
+    font-weight: bold; // 设置卡片标题字体加粗
+  }
+  
+  ion-item {
+    --ion-item-background: transparent; // 设置列表项背景透明
+  }
+  
+  ion-avatar {
+    border-radius: 50%; // 设置头像圆形
+    width: 50px; // 设置头像宽度
+    height: 50px; // 设置头像高度
+  }
+  
+  h2 {
+    font-size: 1em; // 设置二级标题字体大小
+    margin: 0; // 去掉默认外边距
+  }
+  
+  p {
+    font-size: 0.9em; // 设置段落字体大小
+    color: #666; // 设置段落文字颜色
+  }
+  
+  ion-thumbnail {
+    width: 100px; // 设置封面图宽度
+    height: 150px; // 设置封面图高度
+  }

+ 17 - 0
FilmDraw-app/src/app/interact/interact.page.spec.ts

@@ -0,0 +1,17 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { InteractPage } from './interact.page';
+
+describe('InteractPage', () => {
+  let component: InteractPage;
+  let fixture: ComponentFixture<InteractPage>;
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(InteractPage);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 80 - 0
FilmDraw-app/src/app/interact/interact.page.ts

@@ -0,0 +1,80 @@
+import { Component, OnInit, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { 
+  IonContent, IonHeader, IonTitle, IonToolbar,
+  IonCard, IonCardHeader, IonCardTitle, IonCardContent,
+  IonList, IonItem, IonLabel, IonAvatar, IonInput, IonButton, IonIcon
+ } from '@ionic/angular/standalone';
+ import { ExploreContainerComponent } from '../explore-container/explore-container.component';
+
+ interface Character {
+  name: string;
+  avatar: string;
+  description: string;
+}
+
+interface Message {
+  sender: string;
+  text: string;
+}
+
+
+@Component({
+  selector: 'app-interact',
+  templateUrl: './interact.page.html',
+  styleUrls: ['./interact.page.scss'],
+  standalone: true,
+  imports: [
+    IonContent, IonHeader, IonTitle, IonToolbar, CommonModule, FormsModule, ExploreContainerComponent,
+    IonCard, IonCardHeader, IonCardTitle, IonCardContent,
+    IonList, IonItem, IonLabel, IonAvatar, IonInput, IonButton, IonIcon,
+  ],
+  schemas: [CUSTOM_ELEMENTS_SCHEMA], 
+})
+export class InteractPage {
+  characters: Character[] = [
+    {
+      name: '角色A',
+      avatar: 'assets/character-a.png',
+      description: '角色A的简介...',
+    },
+    {
+      name: '角色B',
+      avatar: 'assets/character-b.png',
+      description: '角色B的简介...',
+    },
+    {
+      name: '角色C',
+      avatar: 'assets/character-c.png',
+      description: '角色C的简介...',
+    },
+  ];
+
+  selectedCharacter: Character | null = null; // 当前选择的角色
+  chatMessages: Message[] = []; // 聊天消息列表
+  userMessage: string = ''; // 用户输入的消息
+
+  selectCharacter(character: Character) {
+    this.selectedCharacter = character;
+    this.chatMessages = []; // 清空聊天记录
+  }
+
+  sendMessage() {
+    if (this.userMessage.trim()) {
+      this.chatMessages.push({
+        sender: '您',
+        text: this.userMessage,
+      });
+      this.userMessage = ''; // 清空输入框
+      // 模拟角色的回复
+      setTimeout(() => {
+        this.chatMessages.push({
+          sender: this.selectedCharacter?.name || '角色',
+          text: '这是角色的回复。',
+        });
+      }, 1000);
+    }
+  }
+
+}

+ 3 - 2
FilmDraw-app/src/app/tab1/tab1.page.scss

@@ -26,7 +26,7 @@ ion-buttons {
 
 
 ion-header {
-    background-color: #3880ff; // 设置头部背景色
+    background-color: #ffffff; // 设置头部背景色
     color: white; // 设置头部文字颜色
   }
   
@@ -37,7 +37,8 @@ ion-header {
   }
   
   ion-card-header {
-    background-color: #f0f0f0; // 设置卡片头部背景色
+    background-color: #d8e5fa; // 设置头部背景色
+    color: white; // 设置头部文字颜色
   }
   
   ion-card-title {

+ 9 - 52
FilmDraw-app/src/app/tab3/tab3.page.html

@@ -1,59 +1,16 @@
 <ion-header [translucent]="true">
   <ion-toolbar>
-    <ion-title size="large">角色互动</ion-title>
+    <ion-title size="large">AI追剧搭子</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>
+  <h1>页面:配置路由和参数的聊天页</h1>
+  <ion-button (click)="goChat()">开始页面聊天</ion-button>
+  <h1>组件:直接弹出的聊天组件</h1>
+  <ion-button (click)="openChat()">开始新聊天</ion-button>
+  <ion-button (click)="restoreChat('yHEHqMQDNv')">恢复会话</ion-button>
+  <h1>示例:门诊问诊的智能体示例(ChatPanel组件)</h1>
+  <ion-button (click)="openInquiry()">进入门诊</ion-button>
 
-  <app-explore-container name="Tab 3 page"></app-explore-container> -->
-
-    <!-- 角色选择区 -->
-    <section>
-      <ion-card>
-        <ion-card-header>
-          <ion-card-title>选择角色</ion-card-title>
-        </ion-card-header>
-        <ion-card-content>
-          <ion-list>
-            <ion-item *ngFor="let character of characters" (click)="selectCharacter(character)">
-              <ion-avatar slot="start">
-                <img [src]="character.avatar" alt="{{ character.name }} 头像">
-              </ion-avatar>
-              <ion-label>
-                <h2>{{ character.name }}</h2>
-                <p>{{ character.description }}</p>
-              </ion-label>
-            </ion-item>
-          </ion-list>
-        </ion-card-content>
-      </ion-card>
-    </section>
-  
-    <!-- 角色对话区 -->
-    <section *ngIf="selectedCharacter">
-      <ion-card>
-        <ion-card-header>
-          <ion-card-title>与 {{ selectedCharacter.name }} 对话</ion-card-title>
-        </ion-card-header>
-        <ion-card-content>
-          <div class="chat-area">
-            <div *ngFor="let message of chatMessages" class="message">
-              <strong>{{ message.sender }}:</strong> {{ message.text }}
-            </div>
-          </div>
-          <ion-item>
-            <ion-input [(ngModel)]="userMessage" placeholder="输入您的消息..."></ion-input>
-            <ion-button (click)="sendMessage()" slot="end">
-              <ion-icon name="send"></ion-icon>
-            </ion-button>
-          </ion-item>
-        </ion-card-content>
-      </ion-card>
-    </section>
-</ion-content>
+</ion-content>

+ 8 - 48
FilmDraw-app/src/app/tab3/tab3.page.scss

@@ -1,51 +1,11 @@
 ion-title {
-    flex: 1; // 使标题占据可用空间
-    text-align: left; // 确保文字左对齐
-    margin-left: 16px; // 左侧边距,可以根据需要调整
-    margin-top: 5px;
-  }
+  flex: 1; // 使标题占据可用空间
+  text-align: left; // 确保文字左对齐
+  margin-left: 16px; // 左侧边距,可以根据需要调整
+  margin-top: 5px;
+  color: rgb(71, 68, 68);
+}
 
 ion-header {
-    background-color: #3880ff; // 设置头部背景色
-    color: white; // 设置头部文字颜色
-  }
-  
-  ion-card {
-    margin: 10px; // 设置卡片之间的间距
-    border-radius: 10px; // 设置卡片圆角
-    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); // 添加阴影效果
-  }
-  
-  ion-card-header {
-    background-color: #f0f0f0; // 设置卡片头部背景色
-  }
-  
-  ion-card-title {
-    font-size: 1.2em; // 设置卡片标题字体大小
-    font-weight: bold; // 设置卡片标题字体加粗
-  }
-  
-  ion-item {
-    --ion-item-background: transparent; // 设置列表项背景透明
-  }
-  
-  ion-avatar {
-    border-radius: 50%; // 设置头像圆形
-    width: 50px; // 设置头像宽度
-    height: 50px; // 设置头像高度
-  }
-  
-  h2 {
-    font-size: 1em; // 设置二级标题字体大小
-    margin: 0; // 去掉默认外边距
-  }
-  
-  p {
-    font-size: 0.9em; // 设置段落字体大小
-    color: #666; // 设置段落文字颜色
-  }
-  
-  ion-thumbnail {
-    width: 100px; // 设置封面图宽度
-    height: 150px; // 设置封面图高度
-  }
+  background-color: #3880ff; // 设置头部背景色
+}

+ 109 - 61
FilmDraw-app/src/app/tab3/tab3.page.ts

@@ -1,22 +1,9 @@
-import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
-import { 
-  IonHeader, IonToolbar, IonTitle, IonContent,
-  IonCard, IonCardHeader, IonCardTitle, IonCardContent,
-  IonList, IonItem, IonLabel, IonAvatar, IonInput, IonButton, IonIcon
- } from '@ionic/angular/standalone';
+import { Component } from '@angular/core';
+import { Router } from '@angular/router';
+import { IonHeader, IonToolbar, IonTitle, IonContent, ModalController, IonButton } from '@ionic/angular/standalone';
 import { ExploreContainerComponent } from '../explore-container/explore-container.component';
-import { CommonModule } from '@angular/common';
-
-interface Character {
-  name: string;
-  avatar: string;
-  description: string;
-}
-
-interface Message {
-  sender: string;
-  text: string;
-}
+import { ChatPanelOptions, FmChatModalInput, FmodeChat, FmodeChatMessage, openChatPanelModal } from 'fmode-ng';
+// import { ModalAudioMessageComponent } from 'fmode-ng/lib/aigc/chat/chat-modal-input/modal-audio-message/modal-audio-message.component';
 
 @Component({
   selector: 'app-tab3',
@@ -24,55 +11,116 @@ interface Message {
   styleUrls: ['tab3.page.scss'],
   standalone: true,
   imports: [
-    CommonModule,
     IonHeader, IonToolbar, IonTitle, IonContent, ExploreContainerComponent,
-    IonCard, IonCardHeader, IonCardTitle, IonCardContent,
-    IonList, IonItem, IonLabel, IonAvatar, IonInput, IonButton, IonIcon,
-  ],
-  schemas: [CUSTOM_ELEMENTS_SCHEMA], 
+    IonButton,
+    // ASR语音输入模块
+    FmChatModalInput,
+    // ModalAudioMessageComponent
+  ]
 })
 export class Tab3Page {
-  characters: Character[] = [
-    {
-      name: '角色A',
-      avatar: 'assets/character-a.png',
-      description: '角色A的简介...',
-    },
-    {
-      name: '角色B',
-      avatar: 'assets/character-b.png',
-      description: '角色B的简介...',
-    },
-    {
-      name: '角色C',
-      avatar: 'assets/character-c.png',
-      description: '角色C的简介...',
-    },
-  ];
 
-  selectedCharacter: Character | null = null; // 当前选择的角色
-  chatMessages: Message[] = []; // 聊天消息列表
-  userMessage: string = ''; // 用户输入的消息
+  constructor(
+    private modalCtrl:ModalController,
+    private router:Router,
+    ) {
+
+  }
+
+  /** 示例:问诊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","一名资深的影视剧剧迷,晓晓,年龄22岁");
+              // 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",`
+# 角色设定
+你是一名资深的影视剧剧迷,晓晓,年龄22岁,需要完成一次轻松愉快的看剧感悟分享的陪聊服务。
+
+# 对话环节
+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)
+
+      },
 
-  selectCharacter(character: Character) {
-    this.selectedCharacter = character;
-    this.chatMessages = []; // 清空聊天记录
+    }
+    openChatPanelModal(this.modalCtrl,options)
   }
 
-  sendMessage() {
-    if (this.userMessage.trim()) {
-      this.chatMessages.push({
-        sender: '您',
-        text: this.userMessage,
-      });
-      this.userMessage = ''; // 清空输入框
-      // 模拟角色的回复
-      setTimeout(() => {
-        this.chatMessages.push({
-          sender: this.selectedCharacter?.name || '角色',
-          text: '这是角色的回复。',
-        });
-      }, 1000);
+  restoreChat(chatId:string){
+    let options:ChatPanelOptions = {
+      roleId:"2DXJkRsjXK",
+      chatId:chatId
     }
+    openChatPanelModal(this.modalCtrl,options)
+  }
+
+  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();
+  // }
+
+}