Files
Yi.Admin/Yi.Ai.Vue3/src/stores/modules/chat.ts

182 lines
5.6 KiB
TypeScript
Raw Normal View History

2025-06-17 22:37:37 +08:00
import type { ChatMessageVo } from '@/api/chat/types';
import { defineStore } from 'pinia';
import { getChatList } from '@/api';
import { getUserProfilePicture, systemProfilePicture } from '@/utils/user.ts';
2025-06-17 22:37:37 +08:00
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[]>>({});
2025-12-14 17:25:20 +08:00
/**
*
* @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: [],
};
}
}
2025-06-17 22:37:37 +08:00
const setChatMap = (id: string, data: ChatMessageVo[]) => {
chatMap.value[id] = data?.map((item: ChatMessageVo) => {
const isUser = item.role === 'user';
2025-12-14 17:25:20 +08:00
// 解析消息内容
const { text, images, files } = parseMessageContent(item.content as string);
// 处理思考内容
const thinkContent = extractThkContent(text);
const finalContent = extractThkContentAfter(text);
2025-06-17 22:37:37 +08:00
return {
...item,
id: item.id, // 保留后端返回的消息ID
key: item.id ?? Math.random().toString(36).substring(2, 9),
2025-06-17 22:37:37 +08:00
placement: isUser ? 'end' : 'start',
// 用户消息气泡形状AI消息无气泡形状宽度100%
2025-06-17 22:37:37 +08:00
isMarkdown: !isUser,
// 头像不显示(后续可能会显示)
// avatar: isUser ? getUserProfilePicture() : systemProfilePicture,
// avatarSize: '32px',
2025-06-17 22:37:37 +08:00
typing: false,
reasoning_content: thinkContent,
thinkingStatus: 'end',
2025-12-14 17:25:20 +08:00
content: finalContent,
2025-06-17 22:37:37 +08:00
thinlCollapse: false,
// AI消息使用 noStyle 去除气泡样式
noStyle: !isUser,
shape: isUser ? 'corner' : undefined,
2025-12-14 17:25:20 +08:00
// 保留图片和文件信息(优先使用解析出来的,如果没有则使用原有的)
images: images.length > 0 ? images : item.images,
files: files.length > 0 ? files : item.files,
2025-06-17 22:37:37 +08:00
};
});
};
// 获取当前会话的聊天记录
const requestChatList = async (sessionId: string) => {
// 如果没有 token 则不查询聊天记录
if (!userStore.token)
return;
try {
const res = await getChatList({
sessionId,
userId: userStore.userInfo?.userId as number,
});
2025-06-22 19:09:13 +08:00
if (res.data.items) {
setChatMap(sessionId, res.data.items);
2025-06-17 22:37:37 +08:00
}
}
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,
};
});