/** * 文本|语音聊天框 */ import { create } from "zustand"; import { generateUUID } from '@/utils/index' import { EChatRole, TAnyMessage, TRobotMessage, TMessage } from "@/types/bot"; import {getFormatNow} from '@/utils/timeUtils' type TRobotMessageWithOptionalId = Omit & { msgUk?: string; // 将 messageId 设置为可选 reasoningContent?: string; // 添加 reasoningContent }; const INIT_CURRENT_ROBOT_MSG_UK = '' export interface TextChat { currentRobotMsgUk: string; // 当前正在说话的 AI 机器人 id, 可用于控制是否继续输出文本至当前 message 框内 scrollTop: number; // 控制聊天内容变化后滚动至最底部 autoScroll: boolean list: TAnyMessage[] questions: string[] //推荐问题 sessionId: string|null isReacting: boolean // 是否正在聊天交互 messageRespeaking: boolean; // 当前正在重放回复消息内容 text to speech messageStopHandle: (()=>void) | null; // 停止接收智能文本体消或语音消息的句柄 // 显示聊天历史 setAutoScroll: (b: boolean) => void // 是否自动滚动 genSessionId: () => void // 进入聊天界面后,本次的 sessionId setQuestions: (q:string[]) => void // 设置推荐问题 setScrollTop: (num?: number) => void // 将机器人气泡框推入聊天框 pushRobotMessage: (message: TRobotMessageWithOptionalId) => string; // 将自己发出的气泡框推入聊天框 pushMessage: (content: string) => {msgUk: string, sessionId: string}; // 更新自己发出的气泡框 updateMessage: (content: string, msgUk: string) => string; // 更新机器人汽泡框内的内容实现 gpt 的效果 updateRobotMessage: (content: string, body?: Record, saveStatus?: number, replaceContent?: boolean) => void; getCurrentRobotMessage:() => TRobotMessage|undefined deleteMessage: (msgUk: string) => void; // 清空 destroy: () => void; setMessageRespeaking: (respeaking: boolean)=> void setMessageStopHandle: (func: (()=> void) | null) => void setReacting: (reacting: boolean)=> void updateMessageReaction: (msgId: number, data: {isLike: boolean, isDislike: boolean})=> void } // 新messageId 为 index 加 1 const generateUk = () => { return generateUUID(); } export const useTextChat = create((set, get) => ({ currentRobotMsgUk: INIT_CURRENT_ROBOT_MSG_UK, scrollTop: 0, autoScroll: true, list: [], sessionId: null, questions: [], isReacting: false, messageRespeaking: false, messageStopHandle: null, setMessageRespeaking: (respeaking)=> { set({messageRespeaking: respeaking}) }, setMessageStopHandle: (func)=> { set({messageStopHandle: func}) }, setReacting: (isReacting)=> { set({isReacting}) }, setAutoScroll: (b)=> { set({autoScroll: b}) }, setQuestions: (q:string[])=> { set({questions: q}) }, genSessionId: ()=> { const sessionId = generateUUID(); set({ sessionId }) }, // 重置 destroy: () => { set({ list: [], questions: [], currentRobotMsgUk: INIT_CURRENT_ROBOT_MSG_UK, scrollTop: 0, sessionId: null, }); }, pushRobotMessage: (message) => { const msgUk = generateUk() const sessionId = get().sessionId ?? '' set((state) => { const newRobotMessage = { ...message, msgUk, sessionId, msgTime: getFormatNow(), role: EChatRole.Assistant } as TRobotMessage return { list: [...state.list, newRobotMessage], currentRobotMsgUk: msgUk, scrollTop: state.scrollTop + 1, }; }); setTimeout(()=> { set((state) => { return { scrollTop: state.scrollTop + 1, }; }); }, 100) return msgUk }, pushMessage: (content: string) => { const sessionId = get().sessionId ?? '' const msgUk = generateUk(); const newMessage:TMessage ={ msgUk, sessionId, msgTime: getFormatNow(), content: content, role: EChatRole.User, saveStatus: 0 } set((state) => { return { list: [...state.list, newMessage], autoScroll: true }; }); setTimeout(()=> { set((state) => { return { scrollTop: state.scrollTop + 1, }; }); }, 100) return {msgUk, sessionId} }, setScrollTop: (num?: number)=> { console.log('setScrollTop') set((state) => { if(num!== undefined){ return { scrollTop: num, } } if(state.autoScroll){ return { scrollTop: state.scrollTop + 1, }; } return {scrollTop: state.scrollTop}; }); }, updateMessage: (content, msgUk) => { set((state) => { const updatedList = state.list.map((message) => { if (message.msgUk === msgUk) { return { ...message, content: message.content + content, saveStatus: 0 }; // 更新 content } return message; // 返回未修改的 message }); return { list: updatedList, scrollTop: state.scrollTop + 1 }; // 返回新的状态 }); return msgUk }, updateRobotMessage: (content, body={}, saveStatus: number = 0, replaceContent: boolean = false) => { set((state) => { const updatedList = state.list.map((message) => { if (message.msgUk === state.currentRobotMsgUk) { // 如果是 replaceContent 则认为需要更新全部内容,否则在原 content 上追加 const _content = replaceContent ? content : message.content + content; // 更新消息后, saveStatus 变为 0,说明又需要上报此消息 return { ...message, content: _content, body, saveStatus: saveStatus } as TRobotMessage // 更新 content } return message; // 返回未修改的 message }); return { list: updatedList, scrollTop: state.autoScroll ? state.scrollTop + 1 : state.scrollTop}; // 返回新的状态 }); }, updateMessageReaction: (msgId, data)=> { set((state) => { const updatedList = state.list.map((message) => { if (message.msgId === msgId) { return { ...message, isLike: data.isLike, isDislike: data.isDislike} // 更新 content } return message; // 返回未修改的 message }); return { list: updatedList}; // 返回新的状态 }); }, getCurrentRobotMessage: ()=> { const state = get() const currentMessage = state.list.find((message)=> message.msgUk === state.currentRobotMsgUk) as TRobotMessage return currentMessage }, deleteMessage: (msgUk)=> { set((state) => { // 如果对话框是空的,则删除 const filtered = state.list.filter((message) => { const isEmptyContent = message.content.length <= 0 return (message.msgUk !== msgUk && isEmptyContent) }); return { list: filtered, scrollTop: state.scrollTop + 1 }; // 返回新的状态 }); } }));