178 lines
5.3 KiB
TypeScript
178 lines
5.3 KiB
TypeScript
import type { ChatMessageVo } from '@/api/chat/types';
|
||
import { defineStore } from 'pinia';
|
||
import { getChatList } from '@/api';
|
||
import { getUserProfilePicture, systemProfilePicture } from '@/utils/user.ts';
|
||
import { useUserStore } from './user';
|
||
|
||
export const useChatStore = defineStore('chat', () => {
|
||
const userStore = useUserStore();
|
||
|
||
// 是否开启深度思考
|
||
const isDeepThinking = ref<boolean>(false);
|
||
|
||
const setDeepThinking = (value: boolean) => {
|
||
isDeepThinking.value = value;
|
||
};
|
||
|
||
// 会议ID对应-聊天记录 map对象
|
||
const chatMap = ref<Record<string, ChatMessageVo[]>>({});
|
||
|
||
/**
|
||
* 解析消息内容,提取文本、图片和文件信息
|
||
* @param content - 消息内容,可能是字符串或数组格式的JSON字符串
|
||
* @returns 解析后的文本内容、图片列表和文件列表
|
||
*/
|
||
function parseMessageContent(content: string | any): {
|
||
text: string;
|
||
images: Array<{ url: string; name?: string }>;
|
||
files: Array<{ name: string; size: number }>;
|
||
} {
|
||
let text = '';
|
||
const images: Array<{ url: string; name?: string }> = [];
|
||
const files: Array<{ name: string; size: number }> = [];
|
||
|
||
try {
|
||
// 如果 content 是字符串,尝试解析为 JSON
|
||
let contentArray: any;
|
||
if (typeof content === 'string') {
|
||
// 尝试解析 JSON 数组格式
|
||
if (content.trim().startsWith('[')) {
|
||
contentArray = JSON.parse(content);
|
||
}
|
||
else {
|
||
// 普通文本
|
||
text = content;
|
||
return { text, images, files };
|
||
}
|
||
}
|
||
else {
|
||
contentArray = content;
|
||
}
|
||
|
||
// 如果不是数组,直接返回
|
||
if (!Array.isArray(contentArray)) {
|
||
text = String(content);
|
||
return { text, images, files };
|
||
}
|
||
|
||
// 遍历数组,提取文本和图片
|
||
for (const item of contentArray) {
|
||
if (item.type === 'text') {
|
||
text += item.text || '';
|
||
}
|
||
else if (item.type === 'image_url') {
|
||
if (item.image_url?.url) {
|
||
images.push({
|
||
url: item.image_url.url,
|
||
name: item.image_url.name,
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
// 从文本中提取文件信息(如果有 ATTACHMENT_FILE 标签)
|
||
const fileMatches = text.matchAll(/<ATTACHMENT_FILE>[\s\S]*?<FILE_NAME>(.*?)<\/FILE_NAME>[\s\S]*?<\/ATTACHMENT_FILE>/g);
|
||
for (const match of fileMatches) {
|
||
const fileName = match[1];
|
||
files.push({
|
||
name: fileName,
|
||
size: 0, // 从历史记录中无法获取文件大小
|
||
});
|
||
}
|
||
|
||
// 从文本中移除 ATTACHMENT_FILE 标签及其内容,只保留文件卡片显示
|
||
text = text.replace(/<ATTACHMENT_FILE>[\s\S]*?<\/ATTACHMENT_FILE>/g, '').trim();
|
||
|
||
return { text, images, files };
|
||
}
|
||
catch (error) {
|
||
console.error('解析消息内容失败:', error);
|
||
// 解析失败,返回原始内容
|
||
return {
|
||
text: String(content),
|
||
images: [],
|
||
files: [],
|
||
};
|
||
}
|
||
}
|
||
|
||
const setChatMap = (id: string, data: ChatMessageVo[]) => {
|
||
chatMap.value[id] = data?.map((item: ChatMessageVo) => {
|
||
const isUser = item.role === 'user';
|
||
|
||
// 解析消息内容
|
||
const { text, images, files } = parseMessageContent(item.content as string);
|
||
|
||
// 处理思考内容
|
||
const thinkContent = extractThkContent(text);
|
||
const finalContent = extractThkContentAfter(text);
|
||
|
||
return {
|
||
...item,
|
||
key: item.id,
|
||
placement: isUser ? 'end' : 'start',
|
||
isMarkdown: !isUser,
|
||
avatar: isUser
|
||
? getUserProfilePicture()
|
||
: systemProfilePicture,
|
||
avatarSize: '32px',
|
||
typing: false,
|
||
reasoning_content: thinkContent,
|
||
thinkingStatus: 'end',
|
||
content: finalContent,
|
||
thinlCollapse: false,
|
||
// 保留图片和文件信息(优先使用解析出来的,如果没有则使用原有的)
|
||
images: images.length > 0 ? images : item.images,
|
||
files: files.length > 0 ? files : item.files,
|
||
};
|
||
});
|
||
};
|
||
|
||
// 获取当前会话的聊天记录
|
||
const requestChatList = async (sessionId: string) => {
|
||
// 如果没有 token 则不查询聊天记录
|
||
if (!userStore.token)
|
||
return;
|
||
try {
|
||
const res = await getChatList({
|
||
sessionId,
|
||
userId: userStore.userInfo?.userId as number,
|
||
});
|
||
if (res.data.items) {
|
||
setChatMap(sessionId, res.data.items);
|
||
}
|
||
}
|
||
catch (error) {
|
||
console.error('getChatList:', error);
|
||
}
|
||
};
|
||
|
||
// 对思考中的内容回显做处理
|
||
function extractThkContent(content: string) {
|
||
const regex = /<think>(.*?)<\/think>/s;
|
||
const matchResult = content.match(regex);
|
||
// 把这些内容从 content 中移除
|
||
content = content.replace(regex, '');
|
||
return matchResult?.[1] ?? '';
|
||
}
|
||
|
||
// 如果有 </think> 标签,则把 </think> 之后的 内容从 content 中返回
|
||
function extractThkContentAfter(content: string) {
|
||
if (!content.includes('</think>')) {
|
||
return content;
|
||
}
|
||
const regex = /<\/think>(.*)/s;
|
||
const matchResult = content.match(regex);
|
||
// 把这些内容从 content 中移除
|
||
content = content.replace(regex, '');
|
||
return matchResult?.[1] ?? '';
|
||
}
|
||
|
||
return {
|
||
chatMap,
|
||
requestChatList,
|
||
isDeepThinking,
|
||
setDeepThinking,
|
||
};
|
||
});
|