feat: 前端搭建
This commit is contained in:
18
Yi.Ai.Vue3/src/stores/modules/auth.ts
Normal file
18
Yi.Ai.Vue3/src/stores/modules/auth.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
// 权限状态管理
|
||||
export const useAuthStore = defineStore('auth', () => {
|
||||
// 权限菜单列表
|
||||
const authMenuList = ref<any[]>([]);
|
||||
|
||||
// 请求权限菜单列表
|
||||
const requestAuthMenuList = async () => {
|
||||
// const res = await initDynamicRouter();
|
||||
authMenuList.value = [];
|
||||
};
|
||||
|
||||
return {
|
||||
authMenuList,
|
||||
requestAuthMenuList,
|
||||
};
|
||||
});
|
||||
95
Yi.Ai.Vue3/src/stores/modules/chat.ts
Normal file
95
Yi.Ai.Vue3/src/stores/modules/chat.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import type { ChatMessageVo } from '@/api/chat/types';
|
||||
import { defineStore } from 'pinia';
|
||||
import { getChatList } from '@/api';
|
||||
import { useUserStore } from './user';
|
||||
|
||||
export const useChatStore = defineStore('chat', () => {
|
||||
const userStore = useUserStore();
|
||||
|
||||
// 用户头像
|
||||
const avatar = computed(() => {
|
||||
const userInfo = userStore.userInfo;
|
||||
return userInfo?.avatar || 'https://avatars.githubusercontent.com/u/76239030?v=4';
|
||||
});
|
||||
|
||||
// 是否开启深度思考
|
||||
const isDeepThinking = ref<boolean>(false);
|
||||
|
||||
const setDeepThinking = (value: boolean) => {
|
||||
isDeepThinking.value = value;
|
||||
};
|
||||
|
||||
// 会议ID对应-聊天记录 map对象
|
||||
const chatMap = ref<Record<string, ChatMessageVo[]>>({});
|
||||
|
||||
const setChatMap = (id: string, data: ChatMessageVo[]) => {
|
||||
chatMap.value[id] = data?.map((item: ChatMessageVo) => {
|
||||
const isUser = item.role === 'user';
|
||||
const thinkContent = extractThkContent(item.content as string);
|
||||
return {
|
||||
...item,
|
||||
key: item.id,
|
||||
placement: isUser ? 'end' : 'start',
|
||||
isMarkdown: !isUser,
|
||||
// variant: 'shadow',
|
||||
// shape: 'corner',
|
||||
avatar: isUser
|
||||
? avatar
|
||||
: 'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png',
|
||||
avatarSize: '32px',
|
||||
typing: false,
|
||||
reasoning_content: thinkContent,
|
||||
thinkingStatus: 'end',
|
||||
content: extractThkContentAfter(item.content as string),
|
||||
thinlCollapse: false,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
// 获取当前会话的聊天记录
|
||||
const requestChatList = async (sessionId: string) => {
|
||||
// 如果没有 token 则不查询聊天记录
|
||||
if (!userStore.token)
|
||||
return;
|
||||
try {
|
||||
const res = await getChatList({
|
||||
sessionId,
|
||||
userId: userStore.userInfo?.userId as number,
|
||||
});
|
||||
if (res.rows) {
|
||||
setChatMap(sessionId, res.rows);
|
||||
}
|
||||
}
|
||||
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,
|
||||
};
|
||||
});
|
||||
102
Yi.Ai.Vue3/src/stores/modules/design.ts
Normal file
102
Yi.Ai.Vue3/src/stores/modules/design.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
import type { CollapseType, LayoutType } from '@/config/design';
|
||||
import { defineStore } from 'pinia';
|
||||
import designSetting from '@/config/design';
|
||||
|
||||
const {
|
||||
darkMode: reDarkMode,
|
||||
themeColor,
|
||||
themeColorList,
|
||||
isPageAnimate,
|
||||
pageAnimateType: rePageAnimateType,
|
||||
layout: reLayout,
|
||||
collapseType: reCollapseType,
|
||||
isCollapse: reisCollapse,
|
||||
isSafeAreaHover: reisSafeAreaHover,
|
||||
hasActivatedHover: rehasActivatedHover,
|
||||
} = designSetting;
|
||||
|
||||
export const useDesignStore = defineStore(
|
||||
'design',
|
||||
() => {
|
||||
const darkMode = ref(reDarkMode);
|
||||
const setDarkMode = (modeType: 'light' | 'dark' | 'inverted') => {
|
||||
darkMode.value = modeType;
|
||||
};
|
||||
|
||||
const pageAnimateType = ref(rePageAnimateType);
|
||||
|
||||
const setPageAnimateType = (type: string) => {
|
||||
pageAnimateType.value = type;
|
||||
};
|
||||
|
||||
const layout = ref<LayoutType>(reLayout);
|
||||
|
||||
// 当前只有一个布局,暂时不将这个方法暴露出去
|
||||
// const _setLayout = (layoutType: 'vertical') => {
|
||||
// layout.value = layoutType;
|
||||
// };
|
||||
|
||||
// 折叠状态
|
||||
const collapseType = ref<CollapseType>(reCollapseType);
|
||||
const setCollapseType = (type: CollapseType) => {
|
||||
collapseType.value = type;
|
||||
};
|
||||
|
||||
// 最终是否展开左侧菜单
|
||||
const isCollapse = ref<boolean>(reisCollapse);
|
||||
|
||||
const setCollapse = (collapseFinal: boolean) => {
|
||||
isCollapse.value = collapseFinal;
|
||||
};
|
||||
|
||||
// 折叠按钮是否被悬停
|
||||
const isSafeAreaHover = ref<boolean>(reisSafeAreaHover);
|
||||
|
||||
const setSafeAreaHover = (hover: boolean) => {
|
||||
isSafeAreaHover.value = hover;
|
||||
};
|
||||
|
||||
// 跟踪是否首次激活悬停
|
||||
const hasActivatedHover = ref<boolean>(rehasActivatedHover);
|
||||
|
||||
// 两个监听不要合并
|
||||
watch(
|
||||
() => isCollapse.value,
|
||||
(newValue) => {
|
||||
if (newValue) {
|
||||
hasActivatedHover.value = false;
|
||||
}
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
watch(
|
||||
() => isSafeAreaHover.value,
|
||||
() => {
|
||||
hasActivatedHover.value = true;
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
return {
|
||||
darkMode,
|
||||
setDarkMode,
|
||||
themeColor: ref(themeColor),
|
||||
themeColorList: ref(themeColorList),
|
||||
isPageAnimate: ref(isPageAnimate),
|
||||
pageAnimateType,
|
||||
setPageAnimateType,
|
||||
layout,
|
||||
collapseType,
|
||||
setCollapseType,
|
||||
isCollapse,
|
||||
setCollapse,
|
||||
isSafeAreaHover,
|
||||
setSafeAreaHover,
|
||||
hasActivatedHover,
|
||||
};
|
||||
},
|
||||
{
|
||||
persist: true,
|
||||
},
|
||||
);
|
||||
23
Yi.Ai.Vue3/src/stores/modules/files.ts
Normal file
23
Yi.Ai.Vue3/src/stores/modules/files.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import type { FilesCardProps } from 'vue-element-plus-x/types/FilesCard';
|
||||
// 对话聊天的文件上传列表
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
export const useFilesStore = defineStore('files', () => {
|
||||
const filesList = ref<FilesCardProps & { file: File }[]>([]);
|
||||
|
||||
// 设置文件列表
|
||||
const setFilesList = (list: FilesCardProps & { file: File }[]) => {
|
||||
filesList.value = list;
|
||||
};
|
||||
|
||||
// 根据索引删除 文件
|
||||
const deleteFileByIndex = (index: number) => {
|
||||
filesList.value.splice(index, 1);
|
||||
};
|
||||
|
||||
return {
|
||||
filesList,
|
||||
setFilesList,
|
||||
deleteFileByIndex,
|
||||
};
|
||||
});
|
||||
32
Yi.Ai.Vue3/src/stores/modules/keepAlive.ts
Normal file
32
Yi.Ai.Vue3/src/stores/modules/keepAlive.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
export const useKeepAliveStore = defineStore(
|
||||
'keep-alive',
|
||||
() => {
|
||||
const keepAliveName = ref<string[]>([]);
|
||||
|
||||
const addKeepAliveName = (name: string) => {
|
||||
if (!keepAliveName.value.includes(name)) {
|
||||
keepAliveName.value.push(name);
|
||||
}
|
||||
};
|
||||
|
||||
const removeKeepAliveName = (name: string) => {
|
||||
keepAliveName.value = keepAliveName.value.filter(item => item !== name);
|
||||
};
|
||||
|
||||
const setKeepAliveName = (name: string[]) => {
|
||||
keepAliveName.value = name;
|
||||
};
|
||||
|
||||
return {
|
||||
keepAliveName,
|
||||
addKeepAliveName,
|
||||
removeKeepAliveName,
|
||||
setKeepAliveName,
|
||||
};
|
||||
},
|
||||
{
|
||||
persist: true,
|
||||
},
|
||||
);
|
||||
18
Yi.Ai.Vue3/src/stores/modules/loginForm.ts
Normal file
18
Yi.Ai.Vue3/src/stores/modules/loginForm.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
// 登录表单状态管理
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
type LoginFormType = 'AccountPassword' | 'VerificationCode' | 'RegistrationForm';
|
||||
|
||||
export const useLoginFormStore = defineStore('loginForm', () => {
|
||||
const LoginFormType = ref<LoginFormType>('AccountPassword');
|
||||
|
||||
// 设置登录表单类型
|
||||
const setLoginFormType = (type: LoginFormType) => {
|
||||
LoginFormType.value = type;
|
||||
};
|
||||
|
||||
return {
|
||||
LoginFormType,
|
||||
setLoginFormType,
|
||||
};
|
||||
});
|
||||
34
Yi.Ai.Vue3/src/stores/modules/model.ts
Normal file
34
Yi.Ai.Vue3/src/stores/modules/model.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import type { GetSessionListVO } from '@/api/model/types';
|
||||
import { defineStore } from 'pinia';
|
||||
import { getModelList } from '@/api';
|
||||
|
||||
// 模型管理
|
||||
export const useModelStore = defineStore('model', () => {
|
||||
// 当前模型
|
||||
const currentModelInfo = ref<GetSessionListVO>({});
|
||||
|
||||
// 设置当前模型
|
||||
const setCurrentModelInfo = (modelInfo: GetSessionListVO) => {
|
||||
currentModelInfo.value = modelInfo;
|
||||
};
|
||||
|
||||
// 模型菜单列表
|
||||
const modelList = ref<GetSessionListVO[]>([]);
|
||||
// 请求模型菜单列表
|
||||
const requestModelList = async () => {
|
||||
try {
|
||||
const res = await getModelList();
|
||||
modelList.value = res.data;
|
||||
}
|
||||
catch (error) {
|
||||
console.error('requestModelList错误', error);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
currentModelInfo,
|
||||
setCurrentModelInfo,
|
||||
modelList,
|
||||
requestModelList,
|
||||
};
|
||||
});
|
||||
244
Yi.Ai.Vue3/src/stores/modules/session.ts
Normal file
244
Yi.Ai.Vue3/src/stores/modules/session.ts
Normal file
@@ -0,0 +1,244 @@
|
||||
import type { ChatSessionVo, CreateSessionDTO, GetSessionListParams } from '@/api/session/types';
|
||||
import { ChatLineRound } from '@element-plus/icons-vue';
|
||||
import { defineStore } from 'pinia';
|
||||
import { markRaw } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import {
|
||||
create_session,
|
||||
delete_session,
|
||||
get_session,
|
||||
get_session_list,
|
||||
update_session,
|
||||
} from '@/api';
|
||||
import { useUserStore } from './user';
|
||||
|
||||
export const useSessionStore = defineStore('session', () => {
|
||||
const router = useRouter();
|
||||
const userStore = useUserStore();
|
||||
|
||||
// 当前选中的会话信息
|
||||
const currentSession = ref<ChatSessionVo | null>(null);
|
||||
// 设置当前会话
|
||||
const setCurrentSession = (session: ChatSessionVo | null) => {
|
||||
currentSession.value = session;
|
||||
};
|
||||
|
||||
// 会话列表核心状态
|
||||
const sessionList = ref<ChatSessionVo[]>([]); // 会话数据列表
|
||||
const currentPage = ref(1); // 当前页码(从1开始)
|
||||
const pageSize = ref(25); // 每页显示数量
|
||||
const hasMore = ref(true); // 是否还有更多数据
|
||||
const isLoading = ref(false); // 全局加载状态(初始加载/刷新)
|
||||
const isLoadingMore = ref(false); // 加载更多状态(区分初始加载)
|
||||
|
||||
// 创建新对话(按钮点击)
|
||||
const createSessionBtn = async () => {
|
||||
try {
|
||||
// 清空当前选中会话信息
|
||||
setCurrentSession(null);
|
||||
router.replace({ name: 'chat' });
|
||||
}
|
||||
catch (error) {
|
||||
console.error('createSessionBtn错误:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 获取会话列表(核心分页方法)
|
||||
const requestSessionList = async (page: number = currentPage.value, force: boolean = false) => {
|
||||
// 如果没有token就直接清空
|
||||
if (!userStore.token) {
|
||||
sessionList.value = [];
|
||||
return;
|
||||
}
|
||||
|
||||
if (!force && ((page > 1 && !hasMore.value) || isLoading.value || isLoadingMore.value))
|
||||
return;
|
||||
|
||||
isLoading.value = page === 1; // 第一页时标记为全局加载
|
||||
isLoadingMore.value = page > 1; // 非第一页时标记为加载更多
|
||||
|
||||
try {
|
||||
const params: GetSessionListParams = {
|
||||
userId: userStore.userInfo?.userId as number,
|
||||
pageNum: page,
|
||||
pageSize: pageSize.value,
|
||||
isAsc: 'desc',
|
||||
orderByColumn: 'createTime',
|
||||
};
|
||||
|
||||
const resArr = await get_session_list(params);
|
||||
|
||||
// 预处理会话分组 并添加前缀图标
|
||||
const res = processSessions(resArr.rows);
|
||||
|
||||
const allSessions = new Map(sessionList.value.map(item => [item.id, item])); // 现有所有数据
|
||||
res.forEach(item => allSessions.set(item.id, { ...item })); // 更新/添加数据
|
||||
|
||||
// 按服务端排序重建列表(假设分页数据是按时间倒序,第一页是最新,后续页依次递减)
|
||||
// 此处需根据接口返回的排序规则调整,假设每页数据是递增的(第一页最新,第二页次新,第三页 oldest)
|
||||
if (page === 1) {
|
||||
// 第一页是最新数据,应排在列表前面
|
||||
sessionList.value = [
|
||||
...res, // 新的第一页数据(最新)
|
||||
...Array.from(allSessions.values()).filter(item => !res.some(r => r.id === item.id)), // 保留未被第一页覆盖的旧数据
|
||||
];
|
||||
}
|
||||
else {
|
||||
// 非第一页数据是更旧的数据,追加到列表末尾
|
||||
sessionList.value = [
|
||||
...sessionList.value.filter(item => !res.some(r => r.id === item.id)), // 保留现有数据(除了被当前页更新的)
|
||||
...res, // 追加当前页的新数据(更旧的)
|
||||
];
|
||||
}
|
||||
|
||||
// 判断是否还有更多数据(当前页数据量 < pageSize 则无更多)
|
||||
if (!force)
|
||||
hasMore.value = (res?.length || 0) === pageSize.value;
|
||||
if (!force)
|
||||
currentPage.value = page; // 仅非强制刷新时更新页码
|
||||
}
|
||||
catch (error) {
|
||||
console.error('requestSessionList错误:', error);
|
||||
}
|
||||
finally {
|
||||
isLoading.value = false;
|
||||
isLoadingMore.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 发送消息后创建新会话
|
||||
const createSessionList = async (data: Omit<CreateSessionDTO, 'id'>) => {
|
||||
if (!userStore.token) {
|
||||
router.replace({
|
||||
name: 'chatWithId',
|
||||
params: {
|
||||
id: 'not_login',
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await create_session(data);
|
||||
// 创建会话后立刻查询列表会话
|
||||
// 1. 先找到被修改会话在 sessionList 中的索引(假设 sessionList 是按服务端排序的完整列表)
|
||||
const targetIndex = sessionList.value.findIndex(session => session.id === `${res.data}`);
|
||||
// 2. 计算该会话所在的页码(页大小固定为 pageSize.value)
|
||||
const targetPage
|
||||
= targetIndex >= 0
|
||||
? Math.floor(targetIndex / pageSize.value) + 1 // 索引从0开始,页码从1开始
|
||||
: 1; // 未找到时默认刷新第一页(可能因排序变化导致位置改变)
|
||||
// 3. 刷新目标页数据
|
||||
await requestSessionList(targetPage, true);
|
||||
// 并将当前勾选信息设置为新增的会话信息
|
||||
const newSessionRes = await get_session(`${res.data}`);
|
||||
setCurrentSession(newSessionRes.data);
|
||||
|
||||
// 跳转聊天页
|
||||
router.replace({
|
||||
name: 'chatWithId',
|
||||
params: { id: `${res.data}` },
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
console.error('createSessionList错误:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 加载更多会话(供组件调用)
|
||||
const loadMoreSessions = async () => {
|
||||
if (hasMore.value)
|
||||
await requestSessionList(currentPage.value + 1);
|
||||
};
|
||||
|
||||
// 更新会话(供组件调用)
|
||||
const updateSession = async (item: ChatSessionVo) => {
|
||||
try {
|
||||
await update_session(item);
|
||||
// 1. 先找到被修改会话在 sessionList 中的索引(假设 sessionList 是按服务端排序的完整列表)
|
||||
const targetIndex = sessionList.value.findIndex(session => session.id === item.id);
|
||||
// 2. 计算该会话所在的页码(页大小固定为 pageSize.value)
|
||||
const targetPage
|
||||
= targetIndex >= 0
|
||||
? Math.floor(targetIndex / pageSize.value) + 1 // 索引从0开始,页码从1开始
|
||||
: 1; // 未找到时默认刷新第一页(可能因排序变化导致位置改变)
|
||||
// 3. 刷新目标页数据
|
||||
await requestSessionList(targetPage, true);
|
||||
}
|
||||
catch (error) {
|
||||
console.error('updateSession错误:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 删除会话(供组件调用)
|
||||
const deleteSessions = async (ids: string[]) => {
|
||||
try {
|
||||
await delete_session(ids);
|
||||
// 1. 先找到被修改会话在 sessionList 中的索引(假设 sessionList 是按服务端排序的完整列表)
|
||||
const targetIndex = sessionList.value.findIndex(session => session.id === ids[0]);
|
||||
// 2. 计算该会话所在的页码(页大小固定为 pageSize.value)
|
||||
const targetPage
|
||||
= targetIndex >= 0
|
||||
? Math.floor(targetIndex / pageSize.value) + 1 // 索引从0开始,页码从1开始
|
||||
: 1; // 未找到时默认刷新第一页(可能因排序变化导致位置改变)
|
||||
// 3. 刷新目标页数据
|
||||
await requestSessionList(targetPage, true);
|
||||
}
|
||||
catch (error) {
|
||||
console.error('deleteSessions错误:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 在获取会话列表后添加预处理逻辑(示例)
|
||||
function processSessions(sessions: ChatSessionVo[]) {
|
||||
const currentDate = new Date();
|
||||
|
||||
return sessions.map((session) => {
|
||||
const createDate = new Date(session.createTime!);
|
||||
const diffDays = Math.floor(
|
||||
(currentDate.getTime() - createDate.getTime()) / (1000 * 60 * 60 * 24),
|
||||
);
|
||||
|
||||
// 生成原始分组键(用于排序和分组)
|
||||
let group: string;
|
||||
if (diffDays < 7) {
|
||||
group = '7 天内'; // 用数字前缀确保排序正确
|
||||
}
|
||||
else if (diffDays < 30) {
|
||||
group = '30 天内';
|
||||
}
|
||||
else {
|
||||
const year = createDate.getFullYear();
|
||||
const month = String(createDate.getMonth() + 1).padStart(2, '0');
|
||||
group = `${year}-${month}`; // 格式:2025-05
|
||||
}
|
||||
|
||||
return {
|
||||
...session,
|
||||
group, // 新增分组键字段
|
||||
prefixIcon: markRaw(ChatLineRound), // 图标为静态组件,使用 markRaw 标记为静态组件
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
// 当前选中的会话
|
||||
currentSession,
|
||||
// 设置当前会话
|
||||
setCurrentSession,
|
||||
// 列表状态
|
||||
sessionList,
|
||||
currentPage,
|
||||
pageSize,
|
||||
hasMore,
|
||||
isLoading,
|
||||
isLoadingMore,
|
||||
// 列表方法
|
||||
createSessionBtn,
|
||||
createSessionList,
|
||||
requestSessionList,
|
||||
loadMoreSessions,
|
||||
updateSession,
|
||||
deleteSessions,
|
||||
};
|
||||
});
|
||||
62
Yi.Ai.Vue3/src/stores/modules/user.ts
Normal file
62
Yi.Ai.Vue3/src/stores/modules/user.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import type { LoginUser } from '@/api/auth/types';
|
||||
import { defineStore } from 'pinia';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
export const useUserStore = defineStore(
|
||||
'user',
|
||||
() => {
|
||||
const token = ref<string>();
|
||||
const router = useRouter();
|
||||
const setToken = (value: string) => {
|
||||
token.value = value;
|
||||
};
|
||||
const clearToken = () => {
|
||||
token.value = void 0;
|
||||
};
|
||||
|
||||
const userInfo = ref<LoginUser>();
|
||||
const setUserInfo = (value: LoginUser) => {
|
||||
userInfo.value = value;
|
||||
};
|
||||
const clearUserInfo = () => {
|
||||
userInfo.value = void 0;
|
||||
};
|
||||
|
||||
const logout = async () => {
|
||||
// 如果需要调用接口,可以在这里调用
|
||||
clearToken();
|
||||
clearUserInfo();
|
||||
router.replace({ name: 'chat' });
|
||||
};
|
||||
|
||||
// 新增:登录弹框状态
|
||||
const isLoginDialogVisible = ref(false);
|
||||
|
||||
// 新增:打开弹框方法
|
||||
const openLoginDialog = () => {
|
||||
isLoginDialogVisible.value = true;
|
||||
};
|
||||
|
||||
// 新增:关闭弹框方法(可根据需求扩展)
|
||||
const closeLoginDialog = () => {
|
||||
isLoginDialogVisible.value = false;
|
||||
};
|
||||
|
||||
return {
|
||||
token,
|
||||
setToken,
|
||||
clearToken,
|
||||
userInfo,
|
||||
setUserInfo,
|
||||
clearUserInfo,
|
||||
logout,
|
||||
// 新增:暴露弹框状态和方法
|
||||
isLoginDialogVisible,
|
||||
openLoginDialog,
|
||||
closeLoginDialog,
|
||||
};
|
||||
},
|
||||
{
|
||||
persist: true,
|
||||
},
|
||||
);
|
||||
Reference in New Issue
Block a user