diff --git a/package.json b/package.json index 887f2d2b6fee5007989cb2bfefdcb2e8a5121df0..fa9d2d174d1acbdf69176771ff55249db9b69d7a 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "autosize": "^6.0.1", "cherry-markdown": "^0.8.26", "interactjs": "^1.10.19", + "qx-util": "^0.4.8", "path-browserify": "^1.0.1", "preact": "^10.18.1", "react-tabs": "^6.0.2", @@ -77,7 +78,8 @@ }, "peerDependencies": { "cherry-markdown": "^0.8.26", - "interactjs": "^1.10.19" + "interactjs": "^1.10.19", + "qx-util": "^0.4.8" }, "lint-staged": { "*.ts": "eslint --fix", diff --git a/src/components/chat-container/chat-container.tsx b/src/components/chat-container/chat-container.tsx index 91b88292134ff50c7a13bbb1504dc5bb6bd3d1fa..a829c2d88762872a90b231d6bace130077b4ff3a 100644 --- a/src/components/chat-container/chat-container.tsx +++ b/src/components/chat-container/chat-container.tsx @@ -12,7 +12,11 @@ import { CloseFullScreenSvg, } from '../../icons'; import { AIChatConst } from '../../constants'; -import { IChatToolbarItem, IChatContainerOptions } from '../../interface'; +import { + IChatToolbarItem, + IChatContainerOptions, + IAutoClose, +} from '../../interface'; import { ChatTopics } from '../chat-topics/chat-topics'; import { ChatToolbar } from '../chat-toolbar/chat-toolbar'; import { ChatMinimize } from '../chat-minimize/chat-minimize'; @@ -116,6 +120,22 @@ export interface ChatContainerProps { * @type {IChatContainerOptions} */ containerOptions?: IChatContainerOptions; + + /** + * 自动关闭 + * + * @type {IAutoClose} + * @memberof ChatContainerProps + */ + autoClose?: IAutoClose; + + /** + * @description AI窗口的打开模式 + * - default:默认;minimize:最小化;autoexpand:自动展开 + * @type {('default' | 'minimize' | 'autoexpand')} + * @memberof IChat + */ + openMode?: 'default' | 'minimize' | 'autoexpand'; } interface ChatContainerState { @@ -168,8 +188,37 @@ export class ChatContainer extends Component< // 初始化状态 this.state = { isFullScreen: false, - isMinimize: false, + isMinimize: !!( + props?.openMode === 'minimize' || props?.openMode === 'autoexpand' + ), }; + + props?.aiChat.evt.on('onCompleteMessage', () => { + // 窗口自动关闭 + if (props.autoClose) { + const { mode, duration = 3 } = props.autoClose; + switch (mode) { + case 'minimize': + this.minimize(); + break; + case 'close': + this.close(); + break; + case 'closetime': + setTimeout(() => { + this.close(); + }, duration * 1000); + break; + default: + break; + } + } + + // 窗口的打开模式,请求结束后自动打开 + if (props.openMode === 'autoexpand') { + this.exitMinimize(); + } + }); } ns = new Namespace('chat-container'); diff --git a/src/controller/ai-chat/ai-chat.controller.ts b/src/controller/ai-chat/ai-chat.controller.ts index 1d6b3e703b49e15481071f555b61882494f41f98..3ffef3b4071473ec2050bfb3040aa7b2c32e75b9 100644 --- a/src/controller/ai-chat/ai-chat.controller.ts +++ b/src/controller/ai-chat/ai-chat.controller.ts @@ -1,6 +1,7 @@ /* eslint-disable no-useless-escape */ /* eslint-disable @typescript-eslint/no-explicit-any */ import { Signal, signal } from '@preact/signals'; +import { QXEvent } from 'qx-util'; import { ChatMaterial, ChatMessage } from '../../entity'; import { ITopic, @@ -28,6 +29,16 @@ import { AIChatConst } from '../../constants'; * @class AiChatController */ export class AiChatController { + /** + * 事件触发器 + * @type {EventBase} + */ + evt: QXEvent<{ + [p: string]: (...args: any[]) => any; + }> = new QXEvent<{ + [p: string]: (...args: any[]) => any; + }>(); + /** * 聊天记录 * @@ -276,6 +287,19 @@ export class AiChatController { this.messages.value = [...this.messages.value]; await this.asyncToIndexDB(); } + + // AI回答完成时自动回填 + if (this.opts.autoFill === true) { + const message = + chatMsg || this.messages.value[this.messages.value.length - 1]; + if (message.role === 'ASSISTANT' && message.state === 30) + this.backfill(message); + } + + this.evt.emit('onCompleteMessage', { + messageid: id, + completed, + }); } /** @@ -309,15 +333,6 @@ export class AiChatController { } }); } - // AI回答完成时自动回填 - if (this.opts.autoFill === true) { - const message = - i !== -1 - ? this.messages.value[i] - : this.messages.value[this.messages.value.length - 1]; - if (message.role === 'ASSISTANT' && message.state === 30) - this.backfill(message); - } } /** @@ -733,4 +748,14 @@ export class AiChatController { throw new Error(`不支持${type}推荐类型`); } } + + /** + * 销毁 + * + * @return {*} {Promise} + * @memberof AiChatController + */ + async destroyed(): Promise { + this.evt.reset(); + } } diff --git a/src/controller/chat/chat.controller.ts b/src/controller/chat/chat.controller.ts index 1626f999ebbd2d5e3125cb310df2e5b252228c2e..de8650806d4f937bbfa5bd7ea98e9ee32505b1b9 100644 --- a/src/controller/chat/chat.controller.ts +++ b/src/controller/chat/chat.controller.ts @@ -184,6 +184,8 @@ export class ChatController { contentToolbarItems: chatOptions.contentToolbarItems, footerToolbarItems: chatOptions.footerToolbarItems, questionToolbarItems: chatOptions.questionToolbarItems, + autoClose: chatOptions.autoClose, + openMode: chatOptions.openMode, close: () => { this.close(); if (chatOptions.closed) { @@ -329,6 +331,9 @@ export class ChatController { this.container.remove(); this.container = undefined; } + this.aiTopicMap.forEach(aiChat => { + aiChat.destroyed(); + }); } } diff --git a/src/interface/i-chat-options/i-chat-options.ts b/src/interface/i-chat-options/i-chat-options.ts index f301df161eaf5c3ab26cdb6e86ffa50cd967878b..8e77e954ff262bd702ef8f163a4839193e16d167 100644 --- a/src/interface/i-chat-options/i-chat-options.ts +++ b/src/interface/i-chat-options/i-chat-options.ts @@ -4,6 +4,28 @@ import { IChatToolbarItem } from '../i-chat-toolbar-item/i-chat-toolbar-item'; import { FileUploaderOptions } from '../i-file-uploader-options/i-file-uploader-options'; import { ITopic } from '../i-topic-options/i-topic-options'; +/** + * 自动关闭窗口,用于在提问完成时配置窗口关闭模式 + * + * @export + * @interface IAutoClose + */ +export interface IAutoClose { + /** + * @description 模式 + * - minimize:最小化;close:直接关闭;closetime:定时关闭 + * @type {('minimize' | 'closetime' | 'close')} + */ + mode: 'minimize' | 'close' | 'closetime'; + + /** + * @description 自动关闭延时时间,该值单位为秒(s),当 mode 值为closetime时生效 + * @default 3 + * @type {number} + */ + duration?: number; +} + /** * 聊天数据 * @@ -55,7 +77,23 @@ export interface IChat { * @memberof IChat */ autoQuestion?: boolean; - + + /** + * 自动关闭窗口,用于在提问完成时配置窗口关闭模式 + * + * @type {IAutoClose} + * @memberof IChat + */ + autoClose?: IAutoClose; + + /** + * @description AI窗口的打开模式 + * - default:默认;minimize:默认最小化窗口;autoexpand:默认最小化窗口,当提问完成时自动展开窗口 + * @type {('default' | 'minimize' | 'autoexpand')} + * @memberof IChat + */ + openMode?: 'default' | 'minimize' | 'autoexpand'; + /** * @description 是否自动回填 * - AI回答完成之后是否触发回填,默认关闭 diff --git a/src/interface/index.ts b/src/interface/index.ts index c0a4c24858273c03698ef6cb4ec4761460798507..1f9d4bd4661dbce024ab300c3e954695a5f4d215 100644 --- a/src/interface/index.ts +++ b/src/interface/index.ts @@ -1,5 +1,9 @@ export type { IChatMessage } from './i-chat-message/i-chat-message'; -export type { IChat, IChatOptions } from './i-chat-options/i-chat-options'; +export type { + IChat, + IChatOptions, + IAutoClose, +} from './i-chat-options/i-chat-options'; export type { IMessageItemProvider } from './i-message-item-provider/i-message-item-provider'; export type { IPortalAsyncAction } from './i-portal-async-action/i-portal-async-action'; export type { IChatToolbarItem } from './i-chat-toolbar-item/i-chat-toolbar-item'; diff --git a/vite.config.ts b/vite.config.ts index 3019b59a24ce44c4a076065f5ffbc43a153de2e8..4a4f57bdecbae5a53ebfda0e725cf3fd47bf8d20 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -13,7 +13,7 @@ export default defineConfig({ fileName: format => `index.${format}.js`, }, rollupOptions: { - external: ['cherry-markdown', 'interactjs'], + external: ['cherry-markdown', 'interactjs', 'qx-util'], }, }, server: {