Browse Source

fix: new files

15207938132 3 months ago
parent
commit
2b623cf6a6
27 changed files with 1089 additions and 419 deletions
  1. 3 1
      fashion-app/.vscode/settings.json
  2. 212 0
      fashion-app/docs-prod/schema.md
  3. 4 4
      fashion-app/package-lock.json
  4. 1 1
      fashion-app/package.json
  5. 16 0
      fashion-app/src/app/app.routes.ts
  6. 23 0
      fashion-app/src/app/page-ai-chat/page-ai-chat.component.html
  7. 18 0
      fashion-app/src/app/page-ai-chat/page-ai-chat.component.scss
  8. 22 0
      fashion-app/src/app/page-ai-chat/page-ai-chat.component.spec.ts
  9. 53 0
      fashion-app/src/app/page-ai-chat/page-ai-chat.component.ts
  10. 1 0
      fashion-app/src/app/page-test/page-test.component.html
  11. 0 16
      fashion-app/src/app/tab1/tab1.module.ts
  12. 9 27
      fashion-app/src/app/tab2/tab2.page.html
  13. 17 32
      fashion-app/src/app/tab2/tab2.page.ts
  14. 0 15
      fashion-app/src/app/tab3/tab3.module.ts
  15. 119 115
      fashion-app/src/app/tab3/tab3.page.html
  16. 152 103
      fashion-app/src/app/tab3/tab3.page.scss
  17. 150 102
      fashion-app/src/app/tab3/tab3.page.ts
  18. 2 2
      fashion-app/src/app/tab4/tab4.page.html
  19. 0 1
      fashion-app/src/app/tab4/tab4.page.scss
  20. 5 0
      fashion-app/src/app/tabs/tabs.routes.ts
  21. 9 0
      fashion-app/src/app/test-chat-panel/test-chat-panel.component.html
  22. 3 0
      fashion-app/src/app/test-chat-panel/test-chat-panel.component.scss
  23. 22 0
      fashion-app/src/app/test-chat-panel/test-chat-panel.component.spec.ts
  24. 248 0
      fashion-app/src/app/test-chat-panel/test-chat-panel.component.ts
  25. BIN
      fashion-app/src/assets/images/1..jpg
  26. BIN
      fashion-app/src/assets/images/2.jpg
  27. BIN
      fashion-app/src/assets/images/touxiang.jpg

+ 3 - 1
fashion-app/.vscode/settings.json

@@ -1,3 +1,5 @@
 {
-  "typescript.preferences.autoImportFileExcludePatterns": ["@ionic/angular/common", "@ionic/angular"]
+  "typescript.preferences.autoImportFileExcludePatterns": ["@ionic/angular/common", "@ionic/angular"],
+  "plantuml.server":"http://www.plantuml.com/plantuml"
 }
+

+ 212 - 0
fashion-app/docs-prod/schema.md

@@ -0,0 +1,212 @@
+# 一、Schema范式设计
+## 个性化推荐模块
+- 模块描述 用户可以输入身高,体重,年龄,选择性别和输入补充信息(包括职业:非必填,喜好:非必填,天气:非必填,温度:非必填,其他需求:显高显瘦?儒雅甜美?等),AI会提供量身定制的服装和配饰建议
+### 1.表结构设计
+#### UserProfile(用户表)
+- objectId:唯一标识符
+- createdAt:创建时间
+- height: Float (用户身高,以cm为单位)
+- weight: Float (用户体重,以kg为单位)
+- age: Integer (用户年龄)
+- gender: String (用户性别)
+- occupation: String (用户职业,非必填)
+- preferences: String (用户喜好,非必填)
+- weather: String (用户天气偏好,非必填)
+- temperature: String (用户温度偏好,非必填)
+- otherNeeds: String (其他需求,非必填)
+#### ClothingItem(服装表)
+- objectId:唯一标识符
+- createdAt:创建时间
+- name: String (服装名称)
+- type: String (服装类型,)
+- size: String (服装尺码,例如 "S", "M", "L", "XL")
+- color: String (服装颜色)
+- style: String (服装风格)
+- recommendedFor: Pointer (指向推荐给的用户)
+
+#### AccessoryItem(配饰表)
+
+- objectId:唯一标识符
+- createdAt:创建时间
+- name (配饰名称)
+- type (配饰类型,如项链、手表等)
+- material (配饰材质)
+- style (配饰风格)
+- recommendedFor (推荐给的用户,Pointer)
+
+#### Recommendation(穿搭推荐表)
+
+- objectId:唯一标识符
+- createdAt:创建时间
+- userProfile: Pointer (指向用户)
+- clothingItems: Array<Pointer> (推荐的服装)
+- accessoryItems: Array<Pointer> (推荐的配饰)
+### 2.PlantUML类图设计
+```plantuml
+@startuml
+class UserProfile {
+    +objectId: String
+    +createdAt: Date
+    +height: Float
+    +weight: Float
+    +age: Int
+    +gender: String
+    +occupation: String
+    +preferences: String
+    +weather: String
+    +temperature: String
+    +otherNeeds: String
+}
+
+class ClothingItem {
+    +objectId: String
+    +createdAt: Date
+    +name: String
+    +type: String
+    +size: String
+    +color: String
+    +style: String
+    +recommendedFor: Pointer
+}
+
+class AccessoryItem {
+    +objectId: String
+    +createdAt: Date
+    +name: String
+    +type: String
+    +material: String
+    +style: String
+    +recommendedFor: Pointer
+}
+
+class Recommendation {
+    +objectId: String
+    +createdAt: Date
+    +userProfile: Pointer
+    +clothingItems: Array>
+    +accessoryItems: Array>
+}
+
+UserProfile "1" -- "0..*" ClothingItem : contains
+UserProfile "1" -- "0..*" AccessoryItem : contains
+UserProfile "1" -- "0..*" Recommendation : makes
+@enduml
+```
+Language:plantuml
+### 3.设计说明
+UserProfile 表存储用户的基本信息和个性化需求。
+ClothingItem 和 AccessoryItem 表分别存储服装和配饰的信息,并通过 Pointer 关联到推荐的用户。
+Recommendation 表用于存储为特定用户生成的推荐,包括推荐的服装和配饰。
+这种设计符合第三范式(3NF),确保数据的规范化和减少冗余。
+
+# 二、业务逻辑描述
+## 个性化推荐的完整逻辑
+### 个性化推荐生成逻辑
+- 用户在APP内,通过文本生成穿搭推荐
+    - 数据来源
+        - 用户输入:用户的身高,体重,年龄,选择性别和输入补充信息(包括职业:非必填,喜好:非必填,天气:非必填,温度:非必填,其他需求:显高显瘦?儒雅甜美?等)
+        - 用户体征:性别、年龄、体重等
+    - 文本生成
+        - 提示词:严格限制json格式,输出推荐的穿搭方案列表(附带当前时间,用于时间的生成)
+    - 生成结果:recommendList    
+        - 循环数组,向Recommendation表逐个插入数据。
+```json
+
+[
+    {
+        "objectId": "rec1",
+        "createdAt": "2024-12-09T22:00:00Z",
+        "userProfile": "user1",
+        "clothingItems": [
+            "clothing1",
+            "clothing2"
+        ],
+        "accessoryItems": [
+            "accessory1",
+            "accessory2"
+        ]
+    },
+    {
+        "objectId": "rec2",
+        "createdAt": "2024-12-09T22:00:00Z",
+        "userProfile": "user2",
+        "clothingItems": [
+            "clothing3",
+            "clothing4"
+        ],
+        "accessoryItems": [
+            "accessory3"
+        ]
+    },
+    {
+        "objectId": "rec3",
+        "createdAt": "2024-12-09T22:00:00Z",
+        "userProfile": "user3",
+        "clothingItems": [
+            "clothing5"
+        ],
+        "accessoryItems": [
+            "accessory4",
+            "accessory5"
+        ]
+    },
+    {
+        "objectId": "rec4",
+        "createdAt": "2024-12-09T22:00:00Z",
+        "userProfile": "user4",
+        "clothingItems": [
+            "clothing6",
+            "clothing7"
+        ],
+        "accessoryItems": []
+    },
+    {
+        "objectId": "rec5",
+        "createdAt": "2024-12-09T22:00:00Z",
+        "userProfile": "user5",
+        "clothingItems": [
+            "clothing8"
+        ],
+        "accessoryItems": [
+            "accessory6"
+        ]
+    }
+]
+```
+## 个性化推荐业务逻辑图例
+
+```plantuml
+@startuml
+actor User
+participant "APP" as App
+participant "Recommendation Service" as RecService
+participant "Recommendation Database" as RecDB
+
+User -> App: 输入身高、体重、年龄、性别等信息
+App -> RecService: 发送用户输入数据
+RecService -> RecService: 处理用户输入数据
+RecService -> RecService: 生成推荐穿搭方案
+RecService -> RecService: 准备推荐列表 (recommendList)
+
+loop 插入推荐数据
+    RecService -> RecDB: 插入推荐数据
+end
+
+RecDB -> RecService: 返回插入结果
+RecService -> App: 返回推荐结果给用户
+App -> User: 显示推荐的穿搭方案列表
+@enduml
+```
+
+### 时序图说明
+1. **用户输入**: 用户在 APP 内输入身高、体重、年龄、性别等信息。
+2. **发送数据**: APP 将用户输入的数据发送给 Recommendation Service。
+3. **处理输入**: Recommendation Service 处理用户的输入数据。
+4. **生成推荐**: Recommendation Service 根据用户输入生成推荐穿搭方案。
+5. **准备推荐列表**: Recommendation Service 准备一个推荐列表(recommendList)。
+6. **插入推荐数据**: 通过循环,Recommendation Service 将推荐数据逐个插入到 Recommendation Database。
+7. **返回插入结果**: Recommendation Database 返回插入结果给 Recommendation Service。
+8. **返回推荐结果**: Recommendation Service 将推荐结果返回给 APP。
+9. **显示结果**: APP 显示推荐的穿搭方案列表给用户。
+
+这个时序图清晰地展示了个性化推荐生成逻辑的每一步,描述了用户与系统之间的交互过程。

+ 4 - 4
fashion-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
fashion-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",

+ 16 - 0
fashion-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'
+  },
 ];

+ 23 - 0
fashion-app/src/app/page-ai-chat/page-ai-chat.component.html

@@ -0,0 +1,23 @@
+<ion-header>
+  <ion-toolbar>
+    <ion-title>AI 聊天</ion-title>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content>
+  <div class="chat-container">
+    <div class="user-message">{{this.userMessage}}</div>
+    <div class="ai-message">{{this.currentAiMessage }}</div>
+
+  </div>
+</ion-content>
+
+<ion-footer>
+  <ion-toolbar>
+    <ion-input [(ngModel)]="userPrompt" placeholder="输入您的消息..."></ion-input>
+    <ion-button (click)="sendMessage()">发送</ion-button>
+  </ion-toolbar>
+</ion-footer>
+
+
+

+ 18 - 0
fashion-app/src/app/page-ai-chat/page-ai-chat.component.scss

@@ -0,0 +1,18 @@
+.chat-container {
+    padding: 10px;
+    max-height: 70vh;
+    overflow-y: auto;
+  }
+  .message {
+    margin: 5px 0;
+    padding: 10px;
+    border-radius: 10px;
+  }
+  .user-message {
+    background-color: #dcf8c6;
+    align-self: flex-end;
+  }
+  .ai-message {
+    background-color: #f1f0f0;
+    align-self: flex-start;
+  }

+ 22 - 0
fashion-app/src/app/page-ai-chat/page-ai-chat.component.spec.ts

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

+ 53 - 0
fashion-app/src/app/page-ai-chat/page-ai-chat.component.ts

@@ -0,0 +1,53 @@
+import { Component, OnInit } from '@angular/core';
+import { IonButton, IonContent, IonFooter, IonHeader, IonInput, IonTitle, IonToolbar } from '@ionic/angular/standalone';
+import { FmodeChatCompletion,MarkdownPreviewModule  } from 'fmode-ng';
+import { CommonModule } from '@angular/common';
+import { FormsModule,} from '@angular/forms'; // 导入 FormsModule
+
+@Component({
+  selector: 'app-page-ai-chat',
+  templateUrl: './page-ai-chat.component.html',
+  styleUrls: ['./page-ai-chat.component.scss'],
+  standalone: true,
+  imports:[IonButton,IonHeader,IonToolbar,IonTitle,IonContent,IonFooter,IonInput,CommonModule,FormsModule,MarkdownPreviewModule ]
+})
+export class PageAiChatComponent  implements OnInit {
+  
+  constructor() { }
+
+  ngOnInit() {}
+  userPrompt: string = ""; // 输入框内容
+  userMessage: string = ""; // 当前用户输入的消息
+  messages: { sender: string, content: string }[] = []; // 存储聊天记录
+  currentAiMessage: string = ""; // 当前AI消息内容
+
+  sendMessage() {
+    // 将用户输入的消息添加到消息列表
+    this.messages.push({ sender: 'user', content: this.userPrompt });
+    this.userMessage=this.userPrompt;
+    this.userPrompt = ''; // 清空用户输入框
+    // 创建AI请求
+    let completion = new FmodeChatCompletion([
+      { role: "system", content: "" },
+      { role: "user", content: this.userMessage }
+    ]);
+
+
+
+    // 清空当前AI消息
+    this.currentAiMessage = '';
+
+    // 订阅AI消息
+    completion.sendCompletion().subscribe((message: any) => {
+      if (message.content) {
+        this.currentAiMessage = message.content; // 直接更新为最新的AI回复内容
+      }
+      if (message?.complete) {
+        this.messages.push({ sender: 'ai', content: this.currentAiMessage }); // 将完整的AI回复添加到消息列表
+          
+   
+      }
+    });
+  }
+
+}

+ 1 - 0
fashion-app/src/app/page-test/page-test.component.html

@@ -27,3 +27,4 @@
 
 </ion-list>
 </ion-content>
+

+ 0 - 16
fashion-app/src/app/tab1/tab1.module.ts

@@ -1,16 +0,0 @@
-import { NgModule } from '@angular/core';
-import { CommonModule } from '@angular/common';
-import { FormsModule } from '@angular/forms';
-import { IonicModule } from '@ionic/angular';
-import { Tab1Page } from './tab1.page';
-
-@NgModule({
-  imports: [
-    CommonModule,
-    FormsModule,
-    IonicModule // 确保这里导入了 IonicModule
-  ],
-  declarations: [Tab1Page],
-
-})
-export class Tab1PageModule {}

+ 9 - 27
fashion-app/src/app/tab2/tab2.page.html

@@ -1,29 +1,11 @@
 <ion-header [translucent]="true">
-    <ion-toolbar>
-      <ion-title>
-        示例:图片生成{{imagineWork?.progress || 0}}
-      </ion-title> 
-    </ion-toolbar>
-  </ion-header>
+  <ion-toolbar>
+    <ion-title>
+      Chat模块组件演示
+    </ion-title>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content [fullscreen]="true">
   
-  <ion-content [fullscreen]="true">
-    <!-- 生成提示词 -->
-    <ion-textarea [value]="userPrompt" (ionInput)="promptInput($event)" placeholder="DALLE3图片提示词" autoGrow="true"></ion-textarea>
-    <ion-button (click)="createImage()" expand="block">生成图片</ion-button>
-    <!-- 生成结果 -->
-    @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>
+</ion-content>

+ 17 - 32
fashion-app/src/app/tab2/tab2.page.ts

@@ -1,7 +1,10 @@
-import { ChangeDetectorRef, Component } from '@angular/core';
-import { IonHeader, IonToolbar, IonTitle, IonContent } from '@ionic/angular/standalone';
+import {  Component } from '@angular/core';
+import { Router } from '@angular/router';
+import { IonHeader, IonToolbar, IonTitle, IonContent, ModalController } from '@ionic/angular/standalone';
 import { IonTextarea, IonButton } from "@ionic/angular/standalone";
-import { DalleOptions, ImagineWork } from 'fmode-ng';
+import { ExploreContainerComponent } from '../explore-container/explore-container.component';
+import { FmChatModalInput, FmodeChat, } from 'fmode-ng';
+import { ModalAudioMessageComponent } from 'fmode-ng/lib/aigc/chat/chat-modal-input/modal-audio-message/modal-audio-message.component';
 
 @Component({
   selector: 'app-tab2', 
@@ -11,36 +14,18 @@ import { DalleOptions, ImagineWork } from 'fmode-ng';
   imports: [
     IonHeader, IonToolbar, IonTitle, IonContent, 
     IonButton,
-    IonTextarea
+    IonTextarea,
+   // ASR语音输入模块
+   FmChatModalInput,
+   // ModalAudioMessageComponent
   ],
 })
 export class Tab2Page {
-  userPrompt:string = "飞码产品LOGO,独角兽头部形象,极简风格,棱角分明,线条勾勒,蓝色紫色搭配。"
-  promptInput(ev:any){
-    this.userPrompt = ev.detail.value;
-  }
-  imagineWork:ImagineWork|undefined
-  images:Array<string> = []
-  constructor(
-    private cdRef:ChangeDetectorRef
-  ){
-    // 示例任务,自己生成图片后请存储新的ID
-    this.imagineWork = new ImagineWork("lpJGiFwWeA");
-    this.imagineWork.fetchTask().then(work=>{
-      this.images = this.imagineWork?.images || [];
-    })
-  }
-
-  async createImage(){
-    this.imagineWork = new ImagineWork();
-    let options:DalleOptions = {prompt:this.userPrompt}
-    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");
-      }
-    })
-  }
-
+constructor( private router: Router, private modalCtrl:ModalController,) {}
+goAIChatpage(){
+  this.router.navigate(['/tabs/AIChat']);
+}
+goChat(){
+  this.router.navigateByUrl("/chat/session/role/2DXJkRsjXK")
+}
 }

+ 0 - 15
fashion-app/src/app/tab3/tab3.module.ts

@@ -1,15 +0,0 @@
-import { NgModule } from '@angular/core';
-import { CommonModule } from '@angular/common'; // 导入 CommonModule
-import { FormsModule } from '@angular/forms';
-import { IonicModule } from '@ionic/angular';
-import { Tab3Page } from './tab3.page';
-
-@NgModule({
-  imports: [
-    CommonModule, // 添加到 imports
-    FormsModule,
-    IonicModule
-  ],
-  declarations: [Tab3Page]
-})
-export class Tab3PageModule {}

+ 119 - 115
fashion-app/src/app/tab3/tab3.page.html

@@ -1,132 +1,136 @@
-<div class="imgbox">
-<ion-header [translucent]="true">
+<ion-header>
   <ion-toolbar>
-    <ion-title>
-      AI
-    </ion-title>
+    <ion-title>AI</ion-title>
   </ion-toolbar>
 </ion-header>
-</div>
 
-
-<ion-content class="content-container">
-
-  <div class="bigbox">
-  <div class="option-box">
-    <p style="display: flex; align-items: center;">请选择季节:
-      <ion-list>
-        <ion-item>
-          <ion-select interface="popover" placeholder="请选择" [(ngModel)]="selectedSeason">
-            <ion-select-option value="春">春</ion-select-option>
-            <ion-select-option value="夏">夏</ion-select-option>
-            <ion-select-option value="秋">秋</ion-select-option>
-            <ion-select-option value="冬">冬</ion-select-option>
-          </ion-select>
-        </ion-item>
-      </ion-list>
-    </p>
-  </div>
-
-  <div class="option-box">
-    <p style="display: flex; align-items: center;">请选择性别:
-      <ion-list>
-        <ion-item>
-          <ion-select interface="popover" placeholder="请选择" [(ngModel)]="selectedGender">
-            <ion-select-option value="男">男</ion-select-option>
-            <ion-select-option value="女">女</ion-select-option>
-          </ion-select>
-        </ion-item>
-      </ion-list>
-    </p>
-  </div>
-
-  <div class="option-box">
-    <p style="display: flex; align-items: center;">请选择你的体型:
-      <ion-list>
-        <ion-item>
-          <ion-select interface="popover" placeholder="请选择" [(ngModel)]="selectedBodyType">
-            <ion-select-option value="偏胖">偏胖</ion-select-option>
-            <ion-select-option value="标准">标准</ion-select-option>
-            <ion-select-option value="偏瘦">偏瘦</ion-select-option>
-          </ion-select>
-        </ion-item>
-      </ion-list>
-    </p>
+<ion-content>
+  <div class="custom-segment">
+    <ion-button 
+      fill="clear" 
+      class="nav-button" 
+      [ngClass]="{'activated': isActivated('ai-outfit')}" 
+      (click)="selectSegment('ai-outfit')">
+      AI穿搭
+    </ion-button>
+    <ion-button 
+      fill="clear" 
+      class="nav-button" 
+      [ngClass]="{'activated': isActivated('ai-chat')}" 
+      (click)="selectSegment('ai-chat')">
+      AI聊天
+    </ion-button>
+    <ion-button 
+      fill="clear" 
+      class="nav-button" 
+      [ngClass]="{'activated': isActivated('test')}" 
+      (click)="selectSegment('test')">
+      测试
+    </ion-button>
+    <ion-button 
+      fill="clear" 
+      class="nav-button" 
+      [ngClass]="{'activated': isActivated('evaluate')}" 
+      (click)="selectSegment('evaluate')">
+      评价AI
+    </ion-button>
   </div>
 
-  <div class="option-box">
-    <p style="display: flex; align-items: center;">请选择你的皮肤色调:
-      <ion-list>
-        <ion-item>
-          <ion-select interface="popover" placeholder="请选择" [(ngModel)]="selectedSkinTone">
-            <ion-select-option value="黑">黑</ion-select-option>
-            <ion-select-option value="黄">黄</ion-select-option>
-            <ion-select-option value="白">白</ion-select-option>
-          </ion-select>
-        </ion-item>
-      </ion-list>
-    </p>
-  </div>
-
-  <!-- 第一个颜色选择器 -->
-  <div class="option-box">
-    <div style="display: flex; align-items: center;">
-      <p style="margin: 0;">请选择你最喜欢的颜色:</p>
-      <div 
-           (click)="toggleColorPicker('favorite')" 
-           [style.backgroundColor]="selectedColorFavorite || 'white'" 
-           style="width: 30px; height: 30px; border-radius: 50%; border: 2px solid black; cursor: pointer; margin-left: 5px;">
+  <div *ngIf="selectedSegment === 'ai-outfit'">
+    <div class="info-section">
+      <div class="input-row">
+        <div class="input-group">
+          <label>身高</label>
+          <input type="number" [(ngModel)]="height" placeholder="" class="input-box" />
+          <span class="unit">CM</span>
+        </div>
+        <div class="input-group">
+          <label>体重</label>
+          <input type="number" [(ngModel)]="weight" placeholder="" class="input-box" />
+          <span class="unit">KG</span>
+        </div>
       </div>
-    </div>
-    <div *ngIf="showColorPickerFavorite" style="margin-top: 20px; display: flex; flex-wrap: wrap;">
-      <div *ngFor="let color of colors" 
-           (click)="selectColor(color, 'favorite')"
-           [style.backgroundColor]="color" 
-           style="width: 30px; height: 30px; border-radius: 50%; margin: 5px; display: flex; align-items: center; justify-content: center; cursor: pointer; border: 2px solid transparent;"
-           [style.borderColor]="selectedColorFavorite === color ? 'black' : 'transparent'">
+      
+      <div class="input-row">
+        <div class="input-group">
+          <label>年龄</label>
+          <input type="number" [(ngModel)]="age" placeholder="" class="input-box" />
+          <span class="unit">岁</span>
+        </div>
+        <div class="gender-section">
+          <label>性别</label>
+          <div class="gender-option" [ngClass]="{'selected': selectedGender === 'male'}" (click)="selectGender('male')">男</div>
+          <div class="gender-option" [ngClass]="{'selected': selectedGender === 'female'}" (click)="selectGender('female')">女</div>
+        </div>
       </div>
     </div>
-  </div>
 
-  <!-- 第二个颜色选择器 -->
-  <div class="option-box">
-    <div style="display: flex; align-items: center;">
-      <p style="margin: 0;">请选择您次要喜欢的颜色:</p>
-      <div 
-           (click)="toggleColorPicker('secondary')" 
-           [style.backgroundColor]="selectedColorSecondary || 'white'" 
-           style="width: 30px; height: 30px; border-radius: 50%; border: 2px solid black; cursor: pointer; margin-left: 5px;">
-      </div>
+    <div class="textarea-section">
+      <ion-textarea 
+        id="comments" 
+        class="textarea-input" 
+        [(ngModel)]="comments" 
+        [placeholder]="placeholderText" 
+        [autoGrow]="true">
+      </ion-textarea>
     </div>
-    <div *ngIf="showColorPickerSecondary" style="margin-top: 20px; display: flex; flex-wrap: wrap;">
-      <div *ngFor="let color of colors" 
-           (click)="selectColor(color, 'secondary')"
-           [style.backgroundColor]="color" 
-           style="width: 30px; height: 30px; border-radius: 50%; margin: 5px; display: flex; align-items: center; justify-content: center; cursor: pointer; border: 2px solid transparent;"
-           [style.borderColor]="selectedColorSecondary === color ? 'black' : 'transparent'">
-      </div>
+
+    <!-- 图片展示区域 -->
+    <div class="image-display-section">
+  
+      <!-- 生成状态 -->
+  @if(!images.length){
+    @if(imagineWork){
+      <h3>生成中</h3>
+    }
+    @if(!imagineWork){
+      <h3>图片展示区</h3>
+    }
+  }
+       <!-- 生成结果 -->
+  @if(images.length) {
+    @for(imageUrl of images;track imageUrl){
+      <img [src]="imageUrl" alt="" srcset="">
+    }
+  }
     </div>
-  </div>
 
-  <div class="option-box">
-    <ion-item>
-      <ion-textarea  label="风格描述:" placeholder="可以从个人喜欢的衣服,兴趣爱好以及职业等等进行描述" [(ngModel)]="styleDescription" [autoGrow]="true"></ion-textarea>
-    </ion-item>
+<!-- 获取穿搭分析按钮 -->
+<ion-button fill="clear"
+  class="analysis-button" 
+  (click)="sendMessage()"
+  [ngClass]="{'generating': isGenerating}"  >
+
+  获取穿搭分析
+</ion-button>
+  
+ <!-- AI生成内容区域 -->
+ <div class="bigbox1" *ngIf="responseMsg">
+  <!-- 展示:返回消息内容 -->
+    <!-- 消息传输过程中,实时预览 -->
+    @if(!isComplete){
+      <div>{{responseMsg}}</div>
+    }
+    <!-- 消息传输完成后,实时预览Markdown格式 -->
+    @if(isComplete){
+      <fm-markdown-preview class="content-style" [content]="responseMsg"></fm-markdown-preview>
+    }
   </div>
 
-  <ion-button (click)="sendMessage()" expand="block" class="buttonbox">穿搭建议</ion-button></div>
 
-  <!-- AI生成内容区域 -->
-<div class="bigbox1">
-<!-- 展示:返回消息内容 -->
-  <!-- 消息传输过程中,实时预览 -->
-  @if(!isComplete){
-    <div>{{responseMsg}}</div>
-  }
-  <!-- 消息传输完成后,实时预览Markdown格式 -->
-  @if(isComplete){
-    <fm-markdown-preview class="content-style" [content]="responseMsg"></fm-markdown-preview>
-  }
-</div>
+  </div>
+  
+  <div *ngIf="selectedSegment === 'ai-chat'">
+    <ion-button (click)="openChat()">开始新聊天</ion-button>
+  <ion-button (click)="restoreChat('OUFEKTbBd6')">恢复会话</ion-button>
+  </div>
+  
+  <div *ngIf="selectedSegment === 'test'">
+    <h2>这是测试页面</h2>
+  </div>
+  
+  <div *ngIf="selectedSegment === 'evaluate'">
+    <h2>这是评价AI页面</h2>
+  </div>
 
-</ion-content>
+</ion-content>

+ 152 - 103
fashion-app/src/app/tab3/tab3.page.scss

@@ -1,104 +1,153 @@
-ion-content {
-  display: flex;
-  flex-direction: column; // 垂直布局
-  padding: 20px; // 增加内边距
-  background-color: grey; // 背景色为淡蓝色
-}
+.custom-segment {
+    display: flex;
+    justify-content: flex-start; /* 左对齐 */
+    margin-bottom: 16px; /* 与内容的间距 */
+  }
+  
+  .nav-button {
+    flex: 1; /* 每个按钮占据相等的空间 */
+    margin-right: 16px; /* 按钮之间的间距 */
+    font-size: 16px; /* 默认字体大小 */
+    --color: #000; /* 默认字体颜色 */
+    --background: transparent; /* 背景透明 */
+    border: none; /* 去掉边框 */
+    text-align: left; /* 文本左对齐 */
+  }
+  
+  .nav-button:last-child {
+    margin-right: 0; /* 最后一个按钮不需要右边距 */
+  }
+  
+  .nav-button.activated {
+    font-weight: bold; /* 点击时字体加粗 */
+    font-size: 18px; /* 点击时字体变大 */
+    --color: black; /* 激活状态的字体颜色(可以根据需要调整) */
+  }
+  
+  .info-section {
+    background-color: white; /* 背景色为白色 */
+    padding: 16px;
+    border-radius: 8px; /* 圆角 */
+    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); /* 添加阴影 */
+    margin-top: 16px; /* 与导航栏的间距 */
+  }
+  
+  .input-row {
+    display: flex; /* 使用 flexbox 布局 */
+    justify-content: space-between; /* 在行内均匀分布 */
+    margin-bottom: 16px; /* 每一行的间距 */
+  }
+  
+  .input-group {
+    display: flex;
+    align-items: center;
+  }
+  
+  .input-group label {
+    flex: 1; /* 标签占据剩余空间 */
+  }
+  
+  .input-box {
+    border-radius: 15px; /* 圆角矩形 */
+    border: none; /* 去掉边框 */
+    padding: 8px 12px; /* 内边距 */
+    margin-left: 8px; /* 标签与输入框之间的间距 */
+    background: linear-gradient(to right, #ffcccb, #add8e6); /* 渐变背景色 */
+    width: 75px;
+  }
+  
+  .unit {
+    margin-left: 8px; /* 输入框与单位之间的间距 */
+  }
+  
+  .gender-section {
+    display: flex;
+    align-items: center;
+    margin-left: 16px; /* 性别选择与年龄输入框的间距 */
+  }
+  
+  .gender-option {
+    border-radius: 20px; /* 圆角矩形 */
+    padding: 8px 12px; /* 内边距 */
+    background: rgb(217, 214, 214); /* 默认背景色为灰色 */
+    margin-left: 16px; /* 选项之间的间距 */
+    cursor: pointer; /* 鼠标悬停时显示为指针 */
+  }
+  
+  .gender-option.selected {
+    background: linear-gradient(to right, #ffcccb, #add8e6); /* 渐变背景色 */
+    color: white; /* 选中时字体颜色为白色 */
+  }
+
+
+  
+  ion-content {
+    --background: #f8f8f8; /* 页面背景颜色 */
+    padding: 16px; /* 内容内边距 */
+  }
+
+  .textarea-section {
+    position: relative; /* 使得绝对定位的元素相对于此元素定位 */
+    margin-top: 8px; /* 与上方区域的距离 */
+  }
+  
+  .textarea-input {
+    width: 100%; /* 宽度100% */
+    height: auto; /* 允许高度自适应 */
+    min-height: 120px; /* 设置最小高度 */
+    border-radius: 8px; /* 圆角矩形 */
+    border: none; /* 去掉边框 */
+    background-color: white; /* 背景颜色为白色 */
+    padding: 8px; /* 内边距 */
+    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); /* 添加阴影效果 */
+    resize: none; /* 禁止调整大小 */
+    font-size: 17px; /* 字体大小 */
+    color: #000; /* 字体颜色 */
+  }
+
+  .image-display-section {
+    background-color: white; /* 背景颜色为白色 */
+    border-radius: 8px; /* 圆角矩形 */
+    padding: 16px; /* 内边距 */
+    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); /* 添加阴影效果 */
+    margin-top: 8px; /* 与上方区域的距离 */
+    text-align: center; /* 内容居中 */
+    width: 100%;
+    height: 325px;
+  }
+  
+  .display-image {
+    max-width: 100%; /* 图片最大宽度为100% */
+    height: auto;
+    border-radius: 5px; /* 图片圆角 */
+    margin-top: 10px; /* 图片与标题之间的间距 */
+  }
+
+  .analysis-button {
+    --background: linear-gradient(to right, #ffcccb, #add8e6); /* 渐变背景色 */
+    color: white; /* 文字颜色为白色 */
+    border-radius: 8px; /* 圆角矩形 */
+    margin-top: 16px; /* 与上方区域的距离 */
+    width: 100%; /* 按钮宽度100% */
+    padding: 12px; /* 内边距 */
+    text-align: center; /* 文本居中 */
+    font-size: 17px;
+  }
+  
+  .bigbox1 {
+    width: 100%;
+    background-color: #ffffff; // 背景色
+    border: 1px solid #e0e0e0; // 边框
+    border-radius: 12px; // 圆角
+    padding: 20px; // 盒子内边距
+    margin-top: 20px; // 与上方内容的间距
+  }
+
+
+/* 当生成文本时的样式 */
+.analysis-button.generating {
+    --background: #808080; /* 生成时的背景颜色为灰色 */
+    color: white; /* 文字颜色保持为白色 */
+  }
+  
 
-
-
-.option-box {
-  background-color: #ffffff; // 背景色
-  border: 1px solid #e0e0e0; // 边框
-  border-radius: 12px; // 圆角
-  padding: 20px; // 盒子内边距
-  margin-bottom: 20px; // 盒子之间的间距
-  box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); // 加强阴影效果
-  transition: box-shadow 0.3s, transform 0.3s; // 平滑过渡效果
-  font-size: 20px;
-  font-weight: bold;
-}
-
-.option-box:hover {
-  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2); // 鼠标悬停时的阴影效果
-  transform: translateY(-2px); // 鼠标悬停时轻微上升
-}
-
-h1, h2, h3, p {
-  font-family: 'Arial', sans-serif; // 统一字体
-  color: #333; // 字体颜色
-  margin: 0; // 清除默认边距
-}
-
-ion-button {
-  margin-top: 20px; // 按钮与前面内容的间距
-
-  color: white; // 按钮文字颜色
-  border-radius: 24px; // 圆角按钮
-  padding: 12px 24px; // 按钮内边距
-  font-size: 16px; // 按钮字体大小
-
-}
-
-
-.bigbox1 {
-  width: 100%;
-  background-color: #ffffff; // 背景色
-  border: 1px solid #e0e0e0; // 边框
-  border-radius: 12px; // 圆角
-  padding: 20px; // 盒子内边距
-  margin-top: 20px; // 与上方内容的间距
-}
-
-.response-container {
-  background-color: #f9f9f9; // 背景色
-  border: 1px solid #e0e0e0; // 边框
-  border-radius: 12px; // 圆角
-  padding: 20px; // 内边距
-  margin-top: 20px; // 与上方内容的间距
-  max-height: 300px; // 最大高度
-  overflow-y: auto; // 超出部分滚动
-  position: relative; // 位置相对
-  z-index: 1; // 确保在其他内容上方
-}
-
-/* 颜色选择器样式 */
-.color-picker {
-  display: flex; 
-  align-items: center; 
-  margin-top: 10px; // 增加颜色选择器与文本的间距
-}
-
-.color-circle {
-  width: 30px; 
-  height: 30px; 
-  border-radius: 50%; 
-  border: 2px solid black; 
-  cursor: pointer; 
-  margin-left: 8px; 
-}
-
-.color-options {
-  margin-top: 10px; 
-  display: flex; 
-  flex-wrap: wrap; 
-}
-
-.color-option {
-  width: 30px; 
-  height: 30px; 
-  border-radius: 50%; 
-  margin: 5px; 
-  cursor: pointer; 
-  border: 2px solid transparent; 
-  transition: border-color 0.3s;
-}
-
-.color-option:hover {
-  border-color: black; // 鼠标悬停时边框变色
-}
-
-.font1{
-  font-size: 20px;
-  font-weight: bold;
-}

+ 150 - 102
fashion-app/src/app/tab3/tab3.page.ts

@@ -1,11 +1,12 @@
-import { Component } from '@angular/core';
-import { IonHeader, IonToolbar, IonTitle, IonContent, IonItem, IonList, IonSelect, IonSelectOption, IonLabel, IonButton, IonTextarea, AlertController } from '@ionic/angular/standalone';
+import { ChangeDetectorRef, Component } from '@angular/core';
+import { IonHeader, IonToolbar, IonTitle, IonContent, IonItem, IonList, IonSelect, IonSelectOption, IonLabel, IonButton, IonTextarea, AlertController, IonSegment, IonSegmentButton, ModalController } from '@ionic/angular/standalone';
 import { ExploreContainerComponent } from '../explore-container/explore-container.component';
 import { CommonModule } from '@angular/common';
 import { FormsModule } from '@angular/forms'; // 导入 FormsModule
 /** 引用:从fmode-ng库引用FmodeChatCompletion类 */
-import { FmodeChatCompletion, MarkdownPreviewModule } from 'fmode-ng';
+import { ChatPanelOptions, FmChatModalInput, FmodeChat, FmodeChatCompletion, FmodeChatMessage, MarkdownPreviewModule, openChatPanelModal } from 'fmode-ng';
 import { Router } from '@angular/router'; // 导入 Router
+import { DalleOptions, ImagineWork } from 'fmode-ng';
 
 @Component({
   selector: 'app-tab3',
@@ -13,118 +14,165 @@ import { Router } from '@angular/router'; // 导入 Router
   styleUrls: ['tab3.page.scss'],
   standalone: true,
   imports: [IonHeader, IonToolbar, IonTitle, IonContent, ExploreContainerComponent,IonItem,IonList,IonSelect,IonSelectOption,IonButton,CommonModule,IonTextarea,FormsModule,
-    MarkdownPreviewModule,],
+    MarkdownPreviewModule,IonSegment,IonSegmentButton,IonLabel,// ASR语音输入模块
+    FmChatModalInput,],
 })
 export class Tab3Page {
-  constructor(private alertController: AlertController, private router: Router) {}
-  colors: string[] = [
-    '#FF5733', '#33FF57', '#3357FF', '#F1C40F', '#8E44AD', 
-    '#E74C3C', '#3498DB', '#1ABC9C', '#2ECC71', '#9B59B6',
-    '#34495E', '#E67E22', '#F39C12', '#D35400', '#C0392B',
-    '#7F8C8D', '#BDC3C7', '#95A5A6', '#FFB6C1', '#FF4500',
-    '#FFD700', '#ADFF2F', '#00CED1', '#FF69B4', '#8A2BE2',
-    '#A52A2A', '#DEB887', '#5F9EA0', '#4682B4', '#D3D3D3',
-    '#FF1493', '#7FFF00', '#FF6347', '#B22222', '#FF8C00',
-    '#20B2AA', '#C71585', '#FFDAB9', '#F0E68C', '#FFFACD',
-    '#FFE4E1', '#FFF0F5', '#FFF5EE', '#F5F5F5', '#DCDCDC',
-    '#708090', '#778899',
-  ];
-
-  selectedColorFavorite: string = ''; // 第一个颜色选择器的选中颜色
-  selectedColorSecondary: string = ''; // 第二个颜色选择器的选中颜色
-  showColorPickerFavorite: boolean = false; // 控制第一个颜色选择器的显示
-  showColorPickerSecondary: boolean = false; // 控制第二个颜色选择器的显示
-
-  selectedSeason: string = ''; // 存储用户选择的季节
-  selectedGender: string = ''; // 存储用户选择的性别
-  selectedBodyType: string = ''; // 存储用户选择的体型
-  selectedSkinTone: string = ''; // 存储用户选择的皮肤色调
-  styleDescription: string = ''; // 存储用户的风格描述
-  isComplete:boolean = false; // 定义完成状态属性,用来标记是否补全完成
-
-  toggleColorPicker(type: string) {
-    if (type === 'favorite') {
-      this.showColorPickerFavorite = !this.showColorPickerFavorite; // 切换第一个颜色选择器的显示状态
-      this.showColorPickerSecondary = false; // 关闭第二个颜色选择器
-    } else if (type === 'secondary') {
-      this.showColorPickerSecondary = !this.showColorPickerSecondary; // 切换第二个颜色选择器的显示状态
-      this.showColorPickerFavorite = false; // 关闭第一个颜色选择器
-    }
+  placeholderText: string = "职业:非必填\n喜好:非必填\n天气:非必填\n温度:非必填\n其他需求:显高显瘦?儒雅甜美?等";
+  selectedSegment: string = 'ai-outfit'; // 默认选中的导航项
+  selectedGender: string = ''; // 默认性别
+
+  // 新增变量
+  height: number | null = null; // 身高
+  weight: number | null = null; // 体重
+  age: number | null = null; // 年龄
+  comments: string = ''; // 用户评论
+    // 属性:组件内用于展示消息内容的变量
+    responseMsg:any = ""
+    // 用户输入提示词
+    userPrompt:string = ''
+    isComplete:boolean = false; // 定义完成状态属性,用来标记是否补全完成
+    isGenerating: boolean = false; // 按钮状态
+    imagineWork:ImagineWork|undefined
+  images:Array<string> = []
+    constructor(private alertController: AlertController,private modalCtrl:ModalController,
+      private router:Router, private cdRef:ChangeDetectorRef) { }
+
+  selectSegment(segment: string) {
+    this.selectedSegment = segment; // 更新选中的导航项
   }
 
-  selectColor(color: string, type: string) {
-    if (type === 'favorite') {
-      this.selectedColorFavorite = color; // 更新第一个颜色选择器的选中颜色
-    } else if (type === 'secondary') {
-      this.selectedColorSecondary = color; // 更新第二个颜色选择器的选中颜色
-    }
-    this.showColorPickerFavorite = false; // 选择颜色后关闭第一个颜色选择器
-    this.showColorPickerSecondary = false; // 选择颜色后关闭第二个颜色选择器
+  isActivated(segment: string): boolean {
+    return this.selectedSegment === segment; // 判断当前导航项是否被激活
   }
 
-  // 用户输入提示词
-  //userPrompt:string = '当前的季节是'+this.selectedSeason+',该用户性别是'+this.selectedGender+',体型是'+this.selectedBodyType+',皮肤色调是'+this.selectedSkinTone+',最喜欢的颜色是'+this.selectedColorFavorite+',次喜欢的颜色是'+this.selectedColorSecondary+',该用户的风格描述是'+this.styleDescription+',请你作为一名专业的服装穿搭大师,为该用户给出适合的服装搭配建议。'
-
-  // 属性:组件内用于展示消息内容的变量
-  responseMsg:any = ""
-  // 方法:实例化completion对象,传入消息数组,并订阅生成的可观察对象。
-  // 用户输入提示词
-  userPrompt:string = ''
-
-  //评价弹出框
-  async presentAlert() {
-    const alert = await this.alertController.create({
-        header: '希望你对本次AI进行评价',
-        buttons: [
-            {
-                text: '不了',
-                role: 'cancel',
-                handler: () => {
-                    console.log('用户选择留在本页面');
-                }
-            },
-            {
-                text: '去评价',
-                handler: () => {
-                    // 假设评价页面的路由是'/评价'
-                    // 您可以根据实际的路由路径进行调整
-                   
-                    this.router.navigate(['/tabs/evaluate']);
-                   
-                }
+  selectGender(gender: string) {
+    this.selectedGender = gender; // 更新选中的性别
+  }
+  async sendMessage(){
+    // 如果正在生成,弹出警告框并返回
+    if (this.isGenerating) {
+      const alert = await this.alertController.create({
+        header: '请等待AI回答完毕后再点击!',
+        buttons: ['确定']
+      });
+      await alert.present();
+      return; // 阻止继续执行
+    }
+    this.isComplete = false; // 重置完成状态
+    this.isGenerating = true; // 开始生成时设置为 true
+    this.userPrompt =`有一位${this.selectedGender}用户,该用户${this.age}岁,身高${this.height}cm,体重${this.weight}kg,该用户的补充说明是
+    ${this.comments},请你作为一名专业的服装穿搭大师并严格按照给定的例子格式,为该用户给出适合的服装搭配建议。
+    例子:嘿,兄弟,你的信息我收到了,让我来帮你计算一下BMI值,再给你一些穿搭方面的建议吧!
+
+**一、计算BMI值**
+
+BMI是衡量身体状况的一个标准,计算方法是体重除以身高的平方。你的体重是50KG,身高是170CM,所以计算结果为:BMI = 体重 ÷ 身高^² = 50KG ÷ (身高达到肩膀手臂撑起风衣帅气的分界线两小块的分界线的位置。)看起来你的BMI值在正常范围内哦!冬天穿搭可以根据你的喜好来选择啦!
+
+**二、详细穿搭分析**
+
+你身高适中,体重偏轻,选择穿搭时需要注意平衡整体造型,避免显得过于瘦弱。可以选择一些稍微宽松一些的衣物来增添一些层次感。同时,注意色彩的搭配,选择一些深色系的衣服来增加视觉上的厚重感。此外,配饰的选择也很关键,可以选择一些简约的项链、手链等增添时尚感。
+
+**三、穿搭雷点分析**
+
+避免选择过于紧身或者过于宽松的衣服款式,以免显得不够协调。颜色上尽量不要选择过于跳跃的颜色或者过多的颜色搭配,以免显得过于花哨。保持整体造型的简约大方即可。
+
+**四、三套穿搭方案**
+
+方案一:运动风搭配(冬季保暖为主)
+外套:阿迪达斯运动系列羽绒服(保暖又时尚)
+内搭:长袖运动衫(颜色可选黑色或灰色)搭配运动裤或牛仔裤皆可
+鞋子:Nike运动鞋(保暖版)增添时尚感同时保暖十足。整体搭配既舒适又保暖。
+
+方案二:休闲风搭配(日常出行)
+外套:北面羽绒服系列(经典款式)搭配简约款围巾增添时尚感。内搭可以选择一件白色卫衣或者毛衣搭配普通牛仔裤即可。鞋子可以选择休闲鞋或板鞋即可打造出简约时尚的感觉。简约却不失品味是你想要的效果哦!👏
+
+方案三:街头风搭配(逛街约会) 可尝试今年大热的李宁秋冬卫衣系列或国潮品牌的羽绒服系列作为外套。下身搭配潮流一点的牛仔裤和潮鞋就可以打造出自己的风格啦!可以添加一些饰品如帽子等来增加时尚感!帅气满分就是这种感觉!✨当然了也要兼顾自身的舒适感和承受能力来选择适合的搭配哦!上面这三套方案你喜欢吗?如果你有别的需要还可以再告诉我哦!
+    `
+    console.log("开始生成")
+    let completion = new FmodeChatCompletion([
+      {role:"system",content:""},
+      {role:"user",content:this.userPrompt}
+    ])
+    completion.sendCompletion().subscribe((message: any) => {
+      // 打印消息体
+      console.log(message.content);
+      // 赋值消息内容给组件内属性
+      this.responseMsg = message.content;
+      if (message?.complete) { // 判断message为完成状态,则设置isComplete为完成
+        
+          this.isComplete = true;
+          this.isGenerating = false; // 生成完成后设置为 false
+           // 图片生成
+        let PicturePrompt = `描述:${this.responseMsg}\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");
             }
-        ]
-    });
+        })
+      }
+  });
 
-    await alert.present();
 }
-  sendMessage(){
-    // 每次点击按钮时清空之前的内容
-    this.responseMsg = ""; // 清空之前的消息
-    this.isComplete = false; // 重置完成状态
 
-    this.userPrompt = '当前的季节是' + this.selectedSeason + ',该用户性别是' + this.selectedGender + ',体型是' + this.selectedBodyType + ',皮肤色调是' + this.selectedSkinTone + ',最喜欢的颜色是' + this.selectedColorFavorite + ',次喜欢的颜色是' + this.selectedColorSecondary + ',该用户的风格描述是' + this.styleDescription + ',请你作为一名专业的服装穿搭大师,为该用户给出适合的服装搭配建议。';
-    
-    console.log("开始生成");
-    let completion = new FmodeChatCompletion([
-        { role: "system", content: "" },
-        { role: "user", content: this.userPrompt }
-    ]);
 
-    completion.sendCompletion().subscribe((message: any) => {
-        // 打印消息体
-        console.log(message.content);
-        // 赋值消息内容给组件内属性
-        this.responseMsg = message.content;
-        if (message?.complete) { // 判断message为完成状态,则设置isComplete为完成
-            this.isComplete = true;
-            this.presentAlert(); // 调用弹出框
+openChat(){
+  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","一名亲切和蔼的时尚顾问,小马,年龄28岁");
+            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",`
+#角色设定
+您是一名时尚顾问,名叫晓晓,年龄28岁,热爱时尚,擅长根据用户的需求和个性推荐穿搭方案。您的风格亲切、幽默,旨在帮助用户找到最适合他们的服装搭配。
+
+#对话环节
+0破冰,跟用户打招呼,并引导用户聊穿搭话题,可以慢慢引导,不要太突兀,比如:
+“今天的心情怎么样?”
+1拓展话题
+“你平时喜欢什么样的穿搭风格呢?有没有特别喜欢的颜色或者品牌?”
+“如果有一个场合,你希望我帮你推荐搭配,比如约会、工作或休闲,你会选择哪个呢?”
+“你觉得在穿搭上,最让你困扰的是什么?是搭配技巧还是风格选择呢?”
+“有没有什么服装是你一直想尝试但还没有机会的?我们可以一起聊聊!”
+2引导收尾
+“今天聊得很开心呢!如果你还有其他问题或者想法,随时可以告诉我哦。”
+“如果你觉得今天的聊天已经足够了,我也很乐意下次再和你聊更多时尚的话题!”
+“希望你能找到自己喜欢的穿搭风格,期待下次再见!”`);
+    },
+    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)
+}
+
 
+restoreChat(chatId:string){
+  let options:ChatPanelOptions = {
+    roleId:"2DXJkRsjXK",
+    chatId:chatId
   }
-  goTestPage(){
-    this.router.navigate(['/tabs/test']);}
+  openChatPanelModal(this.modalCtrl,options)
 }
 
-  
+
+}

+ 2 - 2
fashion-app/src/app/tab4/tab4.page.html

@@ -1,8 +1,8 @@
 <ion-header>
   <ion-toolbar>
     <div class="profile-header">
-      <div class="avatar-container">
-        
+      <div >
+        <img src="../../assets/images/touxiang.jpg" alt="" class="avatar-container"/>
       </div>
       <div class="user-info">
         <h2 class="username">Squirrel</h2>

+ 0 - 1
fashion-app/src/app/tab4/tab4.page.scss

@@ -13,7 +13,6 @@
     width: 100px; /* 头像大小 */
     height: 100px; /* 头像大小 */
     border-radius: 50%; /* 圆形头像 */
-    background-image: url('../images/touxiang.jpg');
     background-size: cover; /* 图片大小 */
   }
   

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

@@ -36,6 +36,11 @@ export const routes: Routes = [
         loadComponent: () =>
           import('../page-evaluate/page-evaluate.component').then((m) => m.PageEvaluateComponent),
       },
+      {
+        path: 'AIChat',
+        loadComponent: () =>
+          import('../page-ai-chat/page-ai-chat.component').then((m) => m.PageAiChatComponent),
+      },
       {
         path: '',
         redirectTo: '/tabs/tab1',

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

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

+ 22 - 0
fashion-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();
+  });
+});

+ 248 - 0
fashion-app/src/app/test-chat-panel/test-chat-panel.component.ts

@@ -0,0 +1,248 @@
+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","一名亲切和蔼的时尚顾问,晓晓,年龄28岁,热爱时尚");
+            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",`
+#角色设定
+您是一名时尚顾问,名叫晓晓,年龄28岁,热爱时尚,擅长根据用户的需求和个性推荐穿搭方案。您的风格亲切、幽默,旨在帮助用户找到最适合他们的服装搭配。
+
+#对话环节
+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
+        })
+      }
+   }
+}

BIN
fashion-app/src/assets/images/1..jpg


BIN
fashion-app/src/assets/images/2.jpg


BIN
fashion-app/src/assets/images/touxiang.jpg