|
@@ -0,0 +1,246 @@
|
|
|
+import { Component } from '@angular/core';
|
|
|
+import { ImagineWork, DalleOptions, FmodeChatCompletion} from 'fmode-ng';
|
|
|
+
|
|
|
+@Component({
|
|
|
+ selector: 'app-tab2',
|
|
|
+ templateUrl: './tab2.page.html',
|
|
|
+ styleUrls: ['./tab2.page.scss'],
|
|
|
+})
|
|
|
+export class Tab2Page {
|
|
|
+ isSidebarVisible: boolean = true; // 左边栏显示状态
|
|
|
+ conversations: { id: string; title: string; messages: { content: string, role: string, image?: string }[]; isTitled: boolean }[] = [];
|
|
|
+ currentConversationId: string | null = null; // 当前对话 ID
|
|
|
+ // 动态获取当前对话的消息
|
|
|
+ get currentMessages() {
|
|
|
+ const currentConversation = this.conversations.find(
|
|
|
+ (conversation) => conversation.id === this.currentConversationId
|
|
|
+ );
|
|
|
+ return currentConversation ? currentConversation.messages : [];
|
|
|
+ }
|
|
|
+ userMessage: string = ''; // 用户输入的消息
|
|
|
+ responseMsg: string = ''; // 实时消息内容
|
|
|
+ isComplete: boolean = true; // 消息是否生成完成
|
|
|
+ imagineWork: ImagineWork | undefined; // 当前的生成任务
|
|
|
+ isGenerating: boolean = false; // 当前是否正在生成
|
|
|
+ constructor() {}
|
|
|
+
|
|
|
+ // 切换左边栏显示和隐藏
|
|
|
+ toggleSidebar() {
|
|
|
+ this.isSidebarVisible = !this.isSidebarVisible;
|
|
|
+ console.log(`左边栏 ${this.isSidebarVisible ? '显示' : '隐藏'}`);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建新对话
|
|
|
+ createNewChat() {
|
|
|
+ console.log('创建新对话');
|
|
|
+ const newId = `conversation_${Date.now()}`; // 生成唯一 ID
|
|
|
+ const newConversation = {
|
|
|
+ id: newId,
|
|
|
+ title: '', // 初始标题为空
|
|
|
+ messages: [], // 消息列表为空
|
|
|
+ isTitled: false, // 初始状态:未生成标题
|
|
|
+ };
|
|
|
+ this.conversations.push(newConversation);// 添加到对话列表
|
|
|
+ this.currentConversationId = newId; // 设置为当前对话
|
|
|
+ this.userMessage = ''; // 清空输入框
|
|
|
+ }
|
|
|
+ ngOnInit() {
|
|
|
+ // 初始化时检查是否有对话
|
|
|
+ if (this.conversations.length === 0) {
|
|
|
+ this.createNewChat(); // 自动创建默认对话
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 切换对话
|
|
|
+ switchConversation(conversationId: string) {
|
|
|
+ // 切换到新对话
|
|
|
+ this.currentConversationId = conversationId;
|
|
|
+ this.userMessage = ''; // 清空输入框
|
|
|
+ console.log('切换到对话:', conversationId);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 导出当前对话
|
|
|
+ exportConversation() {
|
|
|
+ console.log('导出当前对话');
|
|
|
+ if (!this.currentConversationId) {
|
|
|
+ console.error('未选择任何对话,无法导出。');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 获取当前对话
|
|
|
+ const currentConversation = this.conversations.find(c => c.id === this.currentConversationId);
|
|
|
+ if (!currentConversation) {
|
|
|
+ console.error('当前对话不存在,无法导出。');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 构建 Markdown 内容
|
|
|
+ const title = currentConversation.title || '未命名对话';
|
|
|
+ let mdContent = `# ${title}\n\n`; // Markdown 标题
|
|
|
+ currentConversation.messages.forEach((message, index) => {
|
|
|
+ const role = message.role === 'user' ? '用户' : 'AI';
|
|
|
+ mdContent += `**${role}:** ${message.content}\n\n`; // 加粗显示角色
|
|
|
+ });
|
|
|
+ // 创建 Blob 对象
|
|
|
+ const blob = new Blob([mdContent], { type: 'text/markdown' });
|
|
|
+ // 创建临时下载链接
|
|
|
+ const url = window.URL.createObjectURL(blob);
|
|
|
+ const a = document.createElement('a');
|
|
|
+ a.href = url;
|
|
|
+ a.download = `${title}.md`; // 设置下载文件名
|
|
|
+ document.body.appendChild(a); // 将 <a> 添加到 DOM 中
|
|
|
+ a.click(); // 触发点击
|
|
|
+ document.body.removeChild(a); // 移除 <a>
|
|
|
+ window.URL.revokeObjectURL(url); // 释放 URL
|
|
|
+ console.log('对话已导出为 Markdown 文件:', title);
|
|
|
+ }
|
|
|
+
|
|
|
+// 发送消息,回答问题
|
|
|
+async sendMessage() {
|
|
|
+ if (this.userMessage.trim() && this.currentConversationId) { // 如果输入的消息不为空
|
|
|
+ const currentMessage = this.userMessage.trim(); // 保留当前输入内容
|
|
|
+ console.log(currentMessage)
|
|
|
+ console.log('发送消息:', currentMessage);
|
|
|
+ const currentConversation = this.conversations.find(c => c.id === this.currentConversationId);
|
|
|
+ if (currentConversation) {
|
|
|
+ // 将用户消息推入聊天列表,并记录其索引
|
|
|
+ const userMessageIndex = currentConversation.messages.push({ content: currentMessage, role: 'user' }) - 1;
|
|
|
+ // 立即清空输入框
|
|
|
+ this.userMessage = '';
|
|
|
+ if (currentMessage.startsWith('请生成图片:')) {
|
|
|
+ // 调用图片生成逻辑
|
|
|
+ const generatedImage = await this.generateImage(currentMessage);
|
|
|
+ if (generatedImage) {
|
|
|
+ // 在用户消息后插入图片生成的内容
|
|
|
+ currentConversation.messages.push({
|
|
|
+ content: '',
|
|
|
+ role: 'other',
|
|
|
+ image: generatedImage,
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ // 插入失败消息
|
|
|
+ currentConversation.messages.push({
|
|
|
+ content: '图片生成失败,请重试。',
|
|
|
+ role: 'other',
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //文本生成逻辑
|
|
|
+ else{
|
|
|
+ // 初始化逐步生成状态
|
|
|
+ this.responseMsg = '';
|
|
|
+ this.isComplete = false; // 设置生成状态为未完成
|
|
|
+ // 调用逐步生成方法
|
|
|
+ this.generateText(currentMessage, currentConversation.messages, userMessageIndex + 1);
|
|
|
+ }
|
|
|
+ }else {
|
|
|
+ console.error('当前对话未找到');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 停止生成
|
|
|
+ stopGeneration() {
|
|
|
+ console.log('终止生成请求');
|
|
|
+ this.isGenerating = false; // 将状态设置为false以停止生成
|
|
|
+ }
|
|
|
+ // 调用 ImagineWork 生成图片
|
|
|
+ private async generateImage(prompt: string): Promise<string | null> {
|
|
|
+ try {
|
|
|
+ console.log('调用图片生成工具...');
|
|
|
+ this.imagineWork = new ImagineWork();
|
|
|
+ const options: DalleOptions = { prompt };
|
|
|
+ console.log('调用生成工具参数:', options);
|
|
|
+ const work = await this.imagineWork.draw(options).toPromise();
|
|
|
+ console.log('生成任务结果:', work?.toJSON());
|
|
|
+ const images = work?.get('images');
|
|
|
+ if (images && images.length > 0) {
|
|
|
+ console.log('图片生成成功:', images[0]);
|
|
|
+ return images[0]; // 返回第一张图片 URL
|
|
|
+ } else {
|
|
|
+ console.error('图片生成失败: 未返回任何图片');
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('图片生成失败:', error);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 调用 FmodeChatCompletion 生成回答
|
|
|
+ private generateText(prompt: string, messages: { content: string, role: string }[], insertIndex: number): void {
|
|
|
+ console.log('调用文本生成工具...');
|
|
|
+ // 设置生成状态为 true
|
|
|
+ this.isGenerating = true;
|
|
|
+ const completion = new FmodeChatCompletion([
|
|
|
+ { role: 'system', content: '您是一位提供详细回答的助手,请帮助用户回答以下问题。' },
|
|
|
+ { role: 'user', content: prompt },
|
|
|
+ ]);
|
|
|
+ // 插入占位消息
|
|
|
+ let newMessage = {role:"other",content:""}
|
|
|
+ messages.splice(insertIndex, 0, newMessage);
|
|
|
+ completion.sendCompletion().subscribe({
|
|
|
+ next: (message: any) => {
|
|
|
+ if (!this.isGenerating) {
|
|
|
+ console.log('生成已被终止,忽略后续内容');
|
|
|
+ return; // 如果状态标记为终止,跳过更新
|
|
|
+ }
|
|
|
+ console.log('生成的部分内容:', message.content);
|
|
|
+ newMessage.content = message?.content;
|
|
|
+ console.log(JSON.stringify(messages))
|
|
|
+ },
|
|
|
+ complete: async () => {
|
|
|
+ console.log('文本生成完成');
|
|
|
+ this.isGenerating = false; // 重置生成状态
|
|
|
+ this.isComplete = true; // 设置生成状态为完成
|
|
|
+ // 检查当前对话是否需要生成标题
|
|
|
+ const currentConversation = this.conversations.find(c => c.id === this.currentConversationId);
|
|
|
+ if (currentConversation && !currentConversation.isTitled) {
|
|
|
+ currentConversation.title = this.generateConversationTitle(currentConversation.messages);
|
|
|
+ currentConversation.isTitled = true; // 标记为已生成标题
|
|
|
+ console.log('对话标题已生成:', currentConversation.title);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ error: (err: any) => {
|
|
|
+ console.error('文本生成失败:', err);
|
|
|
+ newMessage.content = '回答生成失败,请重试。'; // 更新占位消息为错误提示
|
|
|
+ this.isGenerating = false; // 重置生成状态
|
|
|
+ this.isComplete = true; // 重置生成状态
|
|
|
+ },
|
|
|
+ });
|
|
|
+ }
|
|
|
+ // 为对话生成标题
|
|
|
+ private generateConversationTitle(messages: { content: string; role: string }[]): string {
|
|
|
+ const userMessage = messages.find(msg => msg.role === 'user' && msg.content.trim() !== '');
|
|
|
+ if (userMessage) {
|
|
|
+ return `对话:${userMessage.content.slice(0, 5)}...`;
|
|
|
+ }
|
|
|
+ return '对话:无内容';
|
|
|
+ }
|
|
|
+ // 开始语音识别
|
|
|
+ startSpeechRecognition() {
|
|
|
+ // 检查浏览器是否支持 SpeechRecognition
|
|
|
+ if (!('webkitSpeechRecognition' in window)) {
|
|
|
+ alert('抱歉,您的浏览器不支持语音输入功能');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const recognition = new (window as any).webkitSpeechRecognition();
|
|
|
+ recognition.lang = 'zh-CN'; // 设置语言为中文
|
|
|
+ recognition.interimResults = true; // 支持实时识别结果
|
|
|
+ let finalTranscript = ''; // 用于存储最终结果
|
|
|
+ recognition.start();
|
|
|
+ recognition.onresult = (event: any) => {
|
|
|
+ let interimTranscript = '';
|
|
|
+ for (let i = event.resultIndex; i < event.results.length; i++) {
|
|
|
+ if (event.results[i].isFinal) {
|
|
|
+ // 如果结果是最终结果,则存储到 finalTranscript 中
|
|
|
+ finalTranscript += event.results[i][0].transcript;
|
|
|
+ } else {
|
|
|
+ // 如果是临时结果,存储到 interimTranscript 中
|
|
|
+ interimTranscript += event.results[i][0].transcript;
|
|
|
+ }
|
|
|
+ console.log('识别结果:', interimTranscript);
|
|
|
+ }
|
|
|
+ this.userMessage = `${finalTranscript}${interimTranscript}`.trim();
|
|
|
+ };
|
|
|
+ recognition.onerror = (event: any) => {
|
|
|
+ console.error('语音识别错误:', event.error);
|
|
|
+ alert('语音识别失败,请重试!');
|
|
|
+ };
|
|
|
+ }
|
|
|
+}
|