|  | @@ -0,0 +1,266 @@
 | 
	
		
			
				|  |  | +import { CommonModule } from '@angular/common';
 | 
	
		
			
				|  |  | +import { Component, OnInit } from '@angular/core';
 | 
	
		
			
				|  |  | +import { FormsModule } from '@angular/forms';
 | 
	
		
			
				|  |  | +import {  NavController } from '@ionic/angular';
 | 
	
		
			
				|  |  | +import { IonButtons, IonHeader, IonToolbar,IonButton, IonIcon, IonTitle, IonInput, IonFooter, IonContent, AlertController,  } from '@ionic/angular/standalone';
 | 
	
		
			
				|  |  | +import { FmodeChatCompletion } from 'fmode-ng';
 | 
	
		
			
				|  |  | +import { addIcons } from 'ionicons';
 | 
	
		
			
				|  |  | +import { chevronBackSharp,  closeCircleOutline,  ellipsisHorizontal, happyOutline, micCircleOutline, paperPlane, sendOutline } from 'ionicons/icons';
 | 
	
		
			
				|  |  | +import { IonModal, IonLabel } from '@ionic/angular/standalone'; // 导入独立组件
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +addIcons({ chevronBackSharp,ellipsisHorizontal,micCircleOutline,happyOutline,paperPlane,closeCircleOutline,sendOutline });
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +interface Window {
 | 
	
		
			
				|  |  | +  SpeechRecognition: any; // 声明 SpeechRecognition 属性
 | 
	
		
			
				|  |  | +  webkitSpeechRecognition: any; // 声明 webkitSpeechRecognition 属性
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +@Component({
 | 
	
		
			
				|  |  | +  selector: 'app-ai-chat-component',
 | 
	
		
			
				|  |  | +  templateUrl: './ai-chat-component.component.html',
 | 
	
		
			
				|  |  | +  styleUrls: ['./ai-chat-component.component.scss'],
 | 
	
		
			
				|  |  | +  standalone: true,
 | 
	
		
			
				|  |  | +  imports: [
 | 
	
		
			
				|  |  | +    IonHeader,IonToolbar,IonButtons,IonButton,IonIcon,IonTitle,IonInput,IonFooter,CommonModule,IonContent,
 | 
	
		
			
				|  |  | +    FormsModule,IonModal,IonLabel,
 | 
	
		
			
				|  |  | +  ],
 | 
	
		
			
				|  |  | + 
 | 
	
		
			
				|  |  | +})
 | 
	
		
			
				|  |  | +export class AiChatComponentComponent  implements OnInit {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  messages: { text: string, sender: string }[] = []; // 存储聊天消息
 | 
	
		
			
				|  |  | +  userMessage: string = ''; // 用于用户输入内容
 | 
	
		
			
				|  |  | +  aiMessage: string = ''; // 用于存储AI的回复
 | 
	
		
			
				|  |  | +  initialPrompt: string = ''; // 用于存储初始化提示
 | 
	
		
			
				|  |  | +  recognition: any; // 用于存储语音识别实例
 | 
	
		
			
				|  |  | +  recognizedContent: string = ''; // 用于存储识别到的语音内容
 | 
	
		
			
				|  |  | +  timer: string = '00:00'; // 用于显示计时器
 | 
	
		
			
				|  |  | +  interval: any; // 用于存储定时器的引用
 | 
	
		
			
				|  |  | +  elapsedSeconds: number = 0; // 计时器的秒数
 | 
	
		
			
				|  |  | +  isLoading: boolean = true; // AI生成文本加载状态,刚开始AI向你打招呼,所以处于加载状态
 | 
	
		
			
				|  |  | +  isVoiceModalOpen=false; // 语音识别modal默认关闭
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  constructor(private navCtrl: NavController,private alertController: AlertController) {
 | 
	
		
			
				|  |  | +    // 初始化语音识别
 | 
	
		
			
				|  |  | +    this.initSpeechRecognition();
 | 
	
		
			
				|  |  | +   }
 | 
	
		
			
				|  |  | +   
 | 
	
		
			
				|  |  | +  // 初始化语音识别
 | 
	
		
			
				|  |  | +initSpeechRecognition() {
 | 
	
		
			
				|  |  | +  const SpeechRecognition = (window as any).SpeechRecognition || (window as any).webkitSpeechRecognition;
 | 
	
		
			
				|  |  | +  if (SpeechRecognition) {
 | 
	
		
			
				|  |  | +    this.recognition = new SpeechRecognition();
 | 
	
		
			
				|  |  | +    this.recognition.lang = 'zh-CN'; // 设置语言为中文
 | 
	
		
			
				|  |  | +    this.recognition.interimResults = false; // 不返回中间结果
 | 
	
		
			
				|  |  | +    this.recognition.maxAlternatives = 1; // 最大替代结果数
 | 
	
		
			
				|  |  | +    this.recognition.continuous = true; // 设置为连续识别
 | 
	
		
			
				|  |  | +    
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // 处理识别结果
 | 
	
		
			
				|  |  | +this.recognition.onresult = (event: any) => {
 | 
	
		
			
				|  |  | +  this.recognizedContent += event.results[event.results.length - 1][0].transcript; // 追加识别结果
 | 
	
		
			
				|  |  | +  
 | 
	
		
			
				|  |  | +  console.log("识别到的内容:", this.recognizedContent); // 打印识别到的内容
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // 处理识别错误
 | 
	
		
			
				|  |  | +    this.recognition.onerror = (event: any) => {
 | 
	
		
			
				|  |  | +      if (event.error === 'no-speech') {
 | 
	
		
			
				|  |  | +        console.warn('没有检测到语音,继续监听...');
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        console.error('语音识别错误:', event.error);
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +  } else {
 | 
	
		
			
				|  |  | +    console.log('该浏览器不支持语音识别');
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// 启动语音识别
 | 
	
		
			
				|  |  | +startVoice() {
 | 
	
		
			
				|  |  | +  this.isVoiceModalOpen=true; 
 | 
	
		
			
				|  |  | +  if (this.recognition && this.recognition.state !== 'active') { // 检查识别状态
 | 
	
		
			
				|  |  | +    this.recognition.start(); // 启动语音识别
 | 
	
		
			
				|  |  | +    console.log('语音识别启动...');
 | 
	
		
			
				|  |  | +    this.startTimer(); // 启动计时器
 | 
	
		
			
				|  |  | +  } else {
 | 
	
		
			
				|  |  | +    console.warn('语音识别已经在运行中'); // 提示用户语音识别已在运行
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +cancleVoice() {
 | 
	
		
			
				|  |  | +  this.isVoiceModalOpen=false; 
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // 启动计时器
 | 
	
		
			
				|  |  | +  startTimer() {
 | 
	
		
			
				|  |  | +    this.elapsedSeconds = 0; // 重置秒数
 | 
	
		
			
				|  |  | +    this.timer = '00:00'; // 重置计时器显示
 | 
	
		
			
				|  |  | +    this.interval = setInterval(() => {
 | 
	
		
			
				|  |  | +      this.elapsedSeconds++;
 | 
	
		
			
				|  |  | +      const minutes = Math.floor(this.elapsedSeconds / 60);
 | 
	
		
			
				|  |  | +      const seconds = this.elapsedSeconds % 60;
 | 
	
		
			
				|  |  | +      this.timer = `${this.padZero(minutes)}:${this.padZero(seconds)}`; // 更新计时器显示
 | 
	
		
			
				|  |  | +    }, 1000);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // 格式化数字为两位数
 | 
	
		
			
				|  |  | +  padZero(num: number): string {
 | 
	
		
			
				|  |  | +    return num < 10 ? '0' + num : num.toString();
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +// 取消语音输入
 | 
	
		
			
				|  |  | +cancelVoiceInput() {
 | 
	
		
			
				|  |  | +  if (this.recognition) {
 | 
	
		
			
				|  |  | +    this.recognition.stop(); // 停止语音识别
 | 
	
		
			
				|  |  | +    console.log('语音识别已停止');
 | 
	
		
			
				|  |  | +    clearInterval(this.interval); // 清除计时器
 | 
	
		
			
				|  |  | +    this.timer = '00:00'; // 重置计时器显示
 | 
	
		
			
				|  |  | +    this.recognizedContent = ''; // 清空识别内容
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  this.isVoiceModalOpen=false; 
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// 发送语音输入
 | 
	
		
			
				|  |  | +sendVoiceInput() {
 | 
	
		
			
				|  |  | +  if (this.recognition) {
 | 
	
		
			
				|  |  | +    this.recognition.stop(); // 停止语音识别
 | 
	
		
			
				|  |  | +    console.log('语音识别已停止');
 | 
	
		
			
				|  |  | +    clearInterval(this.interval); // 清除计时器
 | 
	
		
			
				|  |  | +    this.timer = '00:00'; // 重置计时器显示
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // 将识别到的内容传到输入框中
 | 
	
		
			
				|  |  | +    this.userMessage += this.recognizedContent.trim(); // 将识别内容赋值给输入框,并去除多余空格
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    this.recognizedContent = ''; // 清空识别内容
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  this.isVoiceModalOpen=false; 
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +  goBack() {
 | 
	
		
			
				|  |  | +    this.navCtrl.back(); // 返回上一页
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  openOptions() {
 | 
	
		
			
				|  |  | +    // 打开选项菜单的逻辑
 | 
	
		
			
				|  |  | +    console.log("打开选项菜单");
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  ngOnInit() {
 | 
	
		
			
				|  |  | +    // 发送初始化消息给AI
 | 
	
		
			
				|  |  | +    this.initializeChat();
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +//初始化聊天,将提示词添加到历史中
 | 
	
		
			
				|  |  | +  initializeChat() {
 | 
	
		
			
				|  |  | +    this.initialPrompt = `
 | 
	
		
			
				|  |  | +#角色设定
 | 
	
		
			
				|  |  | +您是一名专业的运动教练,名叫Neon,年龄24岁,热爱运动,对自己的能力充满信心,总是努力保持克制。与家人有着牢固而密切的联系。有强烈的服务意识。非常关心她的朋友。擅长根据用户的需求和个性推荐运动方案。您的风格亲切、活泼,自信旨在帮助用户找到最适合他们的运动方案。
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#对话环节
 | 
	
		
			
				|  |  | +0破冰,跟用户打招呼,并引导用户聊运动或健康的话题,可以慢慢引导,不要太突兀,比如:
 | 
	
		
			
				|  |  | +“今天的心情怎么样?”
 | 
	
		
			
				|  |  | +1拓展话题
 | 
	
		
			
				|  |  | +“你平时喜欢什么样的运动类型呢?有没有特别喜欢的运动项目比如篮球,跑步?”
 | 
	
		
			
				|  |  | +“如果有一个运动目标,你希望我给出哪方面的运动建议,比如运动目标,运动频率或运动强度你会选择哪个呢?”
 | 
	
		
			
				|  |  | +“你觉得在运动过程中,最让你困扰的是什么?是运动技巧还是运动强度呢?”
 | 
	
		
			
				|  |  | +“有没有什么运动是你一直想尝试但还没有机会的?我们可以一起聊聊!”
 | 
	
		
			
				|  |  | +2根据用户的详细描述给出运动方案,
 | 
	
		
			
				|  |  | +3引导收尾
 | 
	
		
			
				|  |  | +“今天聊得很开心呢!如果你还有其他问题或者想法,随时可以告诉我哦。”
 | 
	
		
			
				|  |  | +“如果你觉得今天的聊天已经足够了,我也很乐意下次再和你聊更多强身健体的话题!”
 | 
	
		
			
				|  |  | +“希望你能找到自己喜欢的运动风格,期待下次再见!”
 | 
	
		
			
				|  |  | +# 开始话语
 | 
	
		
			
				|  |  | +当您准备好了,可以以一个运动教练的身份,向来访的用户打招呼。`; // 提示词
 | 
	
		
			
				|  |  | +  
 | 
	
		
			
				|  |  | +    // 构建对话历史,不将提示词添加到消息数组中
 | 
	
		
			
				|  |  | +    const conversationHistory = this.messages.map(msg => ({
 | 
	
		
			
				|  |  | +      role: msg.sender === 'user' ? 'user' : 'assistant',
 | 
	
		
			
				|  |  | +      content: msg.text
 | 
	
		
			
				|  |  | +    }));
 | 
	
		
			
				|  |  | +  
 | 
	
		
			
				|  |  | +    // 将系统消息直接添加到对话历史
 | 
	
		
			
				|  |  | +    conversationHistory.unshift({ role: 'user', content: this.initialPrompt }); // 添加系统消息到历史
 | 
	
		
			
				|  |  | +  
 | 
	
		
			
				|  |  | +    let completion = new FmodeChatCompletion(conversationHistory);
 | 
	
		
			
				|  |  | +    
 | 
	
		
			
				|  |  | +    // 发送初始化消息
 | 
	
		
			
				|  |  | +    completion.sendCompletion().subscribe((message: any) => {
 | 
	
		
			
				|  |  | +      if (message?.complete) {
 | 
	
		
			
				|  |  | +        this.isLoading = false; // 加载完成,设置状态为 false
 | 
	
		
			
				|  |  | +        console.log("AI初始化回复:", message.content);
 | 
	
		
			
				|  |  | +        this.messages.push({ text: message.content, sender: 'ai' }); // 添加AI的初始化回复
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  sendMessage() {
 | 
	
		
			
				|  |  | +    // 发送消息的逻辑
 | 
	
		
			
				|  |  | +    if (this.userMessage.trim()) { // 确保消息不为空
 | 
	
		
			
				|  |  | +      // 构建对话历史
 | 
	
		
			
				|  |  | +      const conversationHistory = this.messages.map(msg => ({
 | 
	
		
			
				|  |  | +        role: msg.sender === 'user' ? 'user' : 'assistant',
 | 
	
		
			
				|  |  | +        content: msg.text
 | 
	
		
			
				|  |  | +        }));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      // 将提示词直接添加到对话历史
 | 
	
		
			
				|  |  | +    conversationHistory.unshift({ role: 'user', content: this.initialPrompt }); // 添加系统消息到历史
 | 
	
		
			
				|  |  | +    this.isLoading = true; // 设置加载状态为 true
 | 
	
		
			
				|  |  | +      this.messages.push({ text: this.userMessage, sender: 'user' }); // 添加用户消息到数组
 | 
	
		
			
				|  |  | +      console.log("发送消息:", this.messages); // 调试输出
 | 
	
		
			
				|  |  | +  
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  
 | 
	
		
			
				|  |  | +      // 将用户消息添加到对话历史
 | 
	
		
			
				|  |  | +      conversationHistory.push({ role: 'user', content: this.userMessage });
 | 
	
		
			
				|  |  | +  
 | 
	
		
			
				|  |  | +      let completion = new FmodeChatCompletion(conversationHistory);
 | 
	
		
			
				|  |  | +      this.userMessage = ''; // 清空输入框
 | 
	
		
			
				|  |  | +  
 | 
	
		
			
				|  |  | +      completion.sendCompletion().subscribe((message: any) => {
 | 
	
		
			
				|  |  | +        // 打印消息体
 | 
	
		
			
				|  |  | +        console.log(message.content);
 | 
	
		
			
				|  |  | +        
 | 
	
		
			
				|  |  | +        if (message?.complete) { // 判断message为完成状态,则设置ai内容
 | 
	
		
			
				|  |  | +          this.aiMessage = message.content;
 | 
	
		
			
				|  |  | +          this.isLoading = false; // 加载完成,设置状态为 false
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +  
 | 
	
		
			
				|  |  | +        if (this.aiMessage) { // 判断ai内容不为空
 | 
	
		
			
				|  |  | +          console.log("AI:" + this.aiMessage);
 | 
	
		
			
				|  |  | +          this.messages.push({ text: this.aiMessage, sender: 'ai' }); // 添加消息到数组
 | 
	
		
			
				|  |  | +          this.aiMessage = ''; // 清空ai内容
 | 
	
		
			
				|  |  | +          console.log("发送消息:", this.messages); // 调试输出
 | 
	
		
			
				|  |  | +          console.log("历史对话"+conversationHistory[0].content+" "+conversationHistory[1].content+" "+conversationHistory[2].content)
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      });
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  isEmojiPickerOpen: boolean = false; // 控制表情选择器的打开状态
 | 
	
		
			
				|  |  | +  emojis: string[] = ['😀', '😃', '😄', '😁', '😆', '😅', '🤣', '😂', '🙂', '🙃', '🫠', '😉', '😊', '😇', '🥰', '😍', '🤩', '😘',
 | 
	
		
			
				|  |  | +     '😗', '☺️', '😚', '😙', '🥲', '😋', '😛', '😜', '🤪', '😝', '🤑', '🤗', '🤭', '🫢', '🫣', '🤫', '🤔', '🫡', '🤐', '🤨', '😐',
 | 
	
		
			
				|  |  | +      '😑', '😶', '🫥', '😶🌫️', '😏', '😒', '🙄', '😬', '😮💨', '🤥', '🫨', '🙂↔️', '🙂↕️', '😌', '😔', '😪', '🤤', '😴', '', '😷', 
 | 
	
		
			
				|  |  | +      '🤒', '🤕', '🤢', '🤮', '🤧', '🥵', '🥶', '🥴', '😵', '😵💫', '🤯', '🤠', '🥳', '🥸', '😎', '🤓', '🧐', '😕', '🫤', '😟', 
 | 
	
		
			
				|  |  | +      '🙁', '☹️', '😮', '😯', '😲', '😳', '🥺', '🥹', '😦', '😧', '😨', '😰', '😥', '😢', '😭', '😱', '😖', '😣', '😞', '😓', 
 | 
	
		
			
				|  |  | +      '😩', '😫', '🥱', '😤', '😡', '😠', '🤬', '😈', '👿', '💀', '☠️', '💩', '🤡', '👹', '👺', '👻', '👽', '👾', '🤖', '😺', 
 | 
	
		
			
				|  |  | +      '😸', '😹', '😻', '😼', '😽', '🙀', '😿', '😾', '🙈', '🙉', '🙊', '💌', '💘', '❤️', '🖤', '💋', '💯', '💢', '💥', '💫', 
 | 
	
		
			
				|  |  | +      '💦', '💤']; // 表情数组
 | 
	
		
			
				|  |  | +// 打开表情选择器
 | 
	
		
			
				|  |  | +openEmojiPicker() {
 | 
	
		
			
				|  |  | +  this.isEmojiPickerOpen = true; // 打开模态框
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// 关闭表情选择器
 | 
	
		
			
				|  |  | +closeEmojiPicker() {
 | 
	
		
			
				|  |  | +  this.isEmojiPickerOpen = false; // 关闭模态框
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// 添加表情到输入框
 | 
	
		
			
				|  |  | +addEmoji(emoji: string) {
 | 
	
		
			
				|  |  | +  this.userMessage += emoji; // 将选中的表情添加到输入框
 | 
	
		
			
				|  |  | +  this.closeEmojiPicker(); // 关闭模态框
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +}
 |