/** * 将项目内部 ChatMessage 模型适配为 @tdesign-vue-next/chat 的 ChatItemMeta 数据格式 * * ChatList 内部用 ChatMessage 组件渲染每条消息,读取字段:role / name / datetime / avatar / content。 * 注意:ChatList 源码会把任何 truthy 的 status 都重写成 "pending" 导致显示骨架,因此我们不传 status, * 由 ChatPanel.vue 的 #content slot 完全接管内容渲染(包括流式/错误态)。 */ import type { TdChatItemMeta, AIMessageContent, UserMessageContent } from '@tdesign-vue-next/chat' /** 附件信息(图片或文件) */ export interface Attachment { name: string url: string type: 'image' | 'file' mimeType?: string size?: number } /** 项目内部消息模型(与 ChatPanel.vue 中 ChatMessage 保持一致) */ export interface ChatMessage { id: string role: 'user' | 'assistant' content: string streaming: boolean time: string sources?: any[] toolCalls?: any[] error?: boolean feedback?: string | null attachments?: Attachment[] } /** 转换单条消息 */ export function toChatItemMeta(msg: ChatMessage): TdChatItemMeta { const isUser = msg.role === 'user' return { role: isUser ? 'user' : 'assistant', name: isUser ? '我' : 'Support Bot', avatar: isUser ? '' : undefined, datetime: msg.time, content: isUser ? ([{ type: 'text', data: msg.content || '' }] as UserMessageContent[]) : ([{ type: 'markdown', data: msg.content || '' }] as AIMessageContent[]), // 故意不传 status:ChatList 会把 truthy status 强制改为 pending,显示骨架屏 } } /** 批量转换 */ export function toChatData(messages: ChatMessage[]): TdChatItemMeta[] { return messages.map(toChatItemMeta) } /** 根据消息索引判断是否为最后一条 assistant 消息 */ export function isLastAssistant(index: number, messages: ChatMessage[]): boolean { for (let i = messages.length - 1; i >= 0; i--) { if (messages[i].role === 'assistant') return i === index } return false }