feat: ✨ 新增未登录情况下的处理和添加路由动画
This commit is contained in:
parent
edbb5ec5ba
commit
d15519e18f
@ -13,4 +13,6 @@ VITE_WEB_BASE_API = '/prod-api'
|
|||||||
# 是否在打包时开启压缩,支持 gzip 和 brotli
|
# 是否在打包时开启压缩,支持 gzip 和 brotli
|
||||||
VITE_BUILD_COMPRESS = gzip
|
VITE_BUILD_COMPRESS = gzip
|
||||||
|
|
||||||
VITE_API_URL = http://122.51.75.95:6039
|
# VITE_API_URL = http://122.51.75.95:6039
|
||||||
|
VITE_API_URL = http://129.211.24.7:6039
|
||||||
|
|
||||||
|
|||||||
@ -6,8 +6,10 @@ import { reactive, ref } from 'vue';
|
|||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { login } from '@/api';
|
import { login } from '@/api';
|
||||||
import { useUserStore } from '@/stores';
|
import { useUserStore } from '@/stores';
|
||||||
|
import { useSessionStore } from '@/stores/modules/session';
|
||||||
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
const sessionStore = useSessionStore();
|
||||||
|
|
||||||
const formRef = ref<FormInstance>();
|
const formRef = ref<FormInstance>();
|
||||||
|
|
||||||
@ -30,8 +32,10 @@ async function handleSubmit() {
|
|||||||
res.data.token && userStore.setToken(res.data.token);
|
res.data.token && userStore.setToken(res.data.token);
|
||||||
res.data.userInfo && userStore.setUserInfo(res.data.userInfo);
|
res.data.userInfo && userStore.setUserInfo(res.data.userInfo);
|
||||||
ElMessage.success('登录成功');
|
ElMessage.success('登录成功');
|
||||||
router.replace('/');
|
|
||||||
userStore.closeLoginDialog();
|
userStore.closeLoginDialog();
|
||||||
|
// 立刻获取回话列表
|
||||||
|
await sessionStore.requestSessionList(1, true);
|
||||||
|
router.replace('/');
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.error('请求错误:', error);
|
console.error('请求错误:', error);
|
||||||
|
|||||||
@ -15,4 +15,4 @@ export const COLLAPSE_THRESHOLD: number = 600;
|
|||||||
export const SIDE_BAR_WIDTH: number = 280;
|
export const SIDE_BAR_WIDTH: number = 280;
|
||||||
|
|
||||||
// 路由白名单地址[本地存在的路由 staticRouter.ts 中]
|
// 路由白名单地址[本地存在的路由 staticRouter.ts 中]
|
||||||
export const ROUTER_WHITE_LIST: string[] = ['/chat', '/500', '/403', '/404'];
|
export const ROUTER_WHITE_LIST: string[] = ['/chat', '/chat/not_login', '/500', '/403', '/404'];
|
||||||
|
|||||||
@ -18,7 +18,7 @@ const sessionStore = useSessionStore();
|
|||||||
const sessionId = computed(() => route.params?.id);
|
const sessionId = computed(() => route.params?.id);
|
||||||
const conversationsList = computed(() => sessionStore.sessionList);
|
const conversationsList = computed(() => sessionStore.sessionList);
|
||||||
const loadMoreLoading = computed(() => sessionStore.isLoadingMore);
|
const loadMoreLoading = computed(() => sessionStore.isLoadingMore);
|
||||||
const active = computed(() => sessionStore.currentSession?.id);
|
const active = ref<string | undefined>();
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
// 获取会话列表
|
// 获取会话列表
|
||||||
@ -28,6 +28,7 @@ onMounted(async () => {
|
|||||||
const currentSessionRes = await get_session(`${sessionId.value}`);
|
const currentSessionRes = await get_session(`${sessionId.value}`);
|
||||||
// 通过 ID 查询详情,设置当前会话 (因为有分页)
|
// 通过 ID 查询详情,设置当前会话 (因为有分页)
|
||||||
sessionStore.setCurrentSession(currentSessionRes.data);
|
sessionStore.setCurrentSession(currentSessionRes.data);
|
||||||
|
active.value = `${sessionId.value}`;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -176,7 +177,8 @@ function handleMenuCommand(command: string, item: ConversationItem<ChatSessionVo
|
|||||||
:items-style="{
|
:items-style="{
|
||||||
marginLeft: '8px',
|
marginLeft: '8px',
|
||||||
userSelect: 'none',
|
userSelect: 'none',
|
||||||
borderRadius: '16px',
|
borderRadius: '10px',
|
||||||
|
padding: '8px 12px',
|
||||||
}"
|
}"
|
||||||
:items-active-style="{
|
:items-active-style="{
|
||||||
backgroundColor: '#fff',
|
backgroundColor: '#fff',
|
||||||
|
|||||||
@ -3,8 +3,10 @@
|
|||||||
import Popover from '@/components/Popover/index.vue';
|
import Popover from '@/components/Popover/index.vue';
|
||||||
import SvgIcon from '@/components/SvgIcon/index.vue';
|
import SvgIcon from '@/components/SvgIcon/index.vue';
|
||||||
import { useUserStore } from '@/stores';
|
import { useUserStore } from '@/stores';
|
||||||
|
import { useSessionStore } from '@/stores/modules/session';
|
||||||
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
const sessionStore = useSessionStore();
|
||||||
const src = computed(
|
const src = computed(
|
||||||
() => userStore.userInfo?.avatar ?? 'https://avatars.githubusercontent.com/u/76239030',
|
() => userStore.userInfo?.avatar ?? 'https://avatars.githubusercontent.com/u/76239030',
|
||||||
);
|
);
|
||||||
@ -62,9 +64,12 @@ function handleClick(item: any) {
|
|||||||
roundButton: true,
|
roundButton: true,
|
||||||
autofocus: false,
|
autofocus: false,
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(async () => {
|
||||||
// 在这里执行退出方法
|
// 在这里执行退出方法
|
||||||
userStore.logout();
|
await userStore.logout();
|
||||||
|
// 清空回话列表并回到默认页
|
||||||
|
await sessionStore.requestSessionList(1, true);
|
||||||
|
await sessionStore.createSessionBtn();
|
||||||
ElMessage({
|
ElMessage({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
message: '退出成功',
|
message: '退出成功',
|
||||||
|
|||||||
@ -14,7 +14,7 @@ function handleCreatChat() {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="create-chat-container flex-center flex-none p-6px pl-8px pr-8px c-#0057ff b-#0057ff b-rounded-12px border-1px hover:bg-#0057ff hover:c-#fff hover:b-#fff hover:cursor-pointer border-solid"
|
class="create-chat-container flex-center flex-none p-6px pl-8px pr-8px c-#0057ff b-#0057ff b-rounded-12px border-1px hover:bg-#0057ff hover:c-#fff hover:b-#fff hover:cursor-pointer border-solid select-none"
|
||||||
@click="handleCreatChat"
|
@click="handleCreatChat"
|
||||||
>
|
>
|
||||||
<el-icon size="12" class="flex-center flex-none w-14px h-14px">
|
<el-icon size="12" class="flex-center flex-none w-14px h-14px">
|
||||||
|
|||||||
@ -14,6 +14,8 @@ const userStore = useUserStore();
|
|||||||
const designStore = useDesignStore();
|
const designStore = useDesignStore();
|
||||||
const sessionStore = useSessionStore();
|
const sessionStore = useSessionStore();
|
||||||
|
|
||||||
|
const currentSession = computed(() => sessionStore.currentSession);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 全局设置侧边栏默认宽度 (这个是不变的,一开始就设置)
|
// 全局设置侧边栏默认宽度 (这个是不变的,一开始就设置)
|
||||||
document.documentElement.style.setProperty(`--sidebar-default-width`, `${SIDE_BAR_WIDTH}px`);
|
document.documentElement.style.setProperty(`--sidebar-default-width`, `${SIDE_BAR_WIDTH}px`);
|
||||||
@ -55,7 +57,7 @@ onKeyStroke(event => event.ctrlKey && event.key.toLowerCase() === 'k', handleCtr
|
|||||||
>
|
>
|
||||||
<Collapse />
|
<Collapse />
|
||||||
<CreateChat />
|
<CreateChat />
|
||||||
<div class="w-0.5px h-30px bg-[rgba(217,217,217)]" />
|
<div v-if="currentSession" class="w-0.5px h-30px bg-[rgba(217,217,217)]" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 中间 -->
|
<!-- 中间 -->
|
||||||
|
|||||||
@ -1,10 +1,21 @@
|
|||||||
<!-- Main -->
|
<!-- Main -->
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
import { useDesignStore } from '@/stores';
|
import { useDesignStore } from '@/stores';
|
||||||
import { useKeepAliveStore } from '@/stores/modules/keepAlive';
|
import { useKeepAliveStore } from '@/stores/modules/keepAlive';
|
||||||
|
|
||||||
const designStore = useDesignStore();
|
const designStore = useDesignStore();
|
||||||
const keepAliveStore = useKeepAliveStore();
|
const keepAliveStore = useKeepAliveStore();
|
||||||
|
const useroute = useRoute();
|
||||||
|
|
||||||
|
const transitionName = computed(() => {
|
||||||
|
if (useroute.meta.isDefaultChat) {
|
||||||
|
return 'slide';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return designStore.pageAnimateType;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// 刷新当前路由页面缓存方法
|
// 刷新当前路由页面缓存方法
|
||||||
const isRouterShow = ref(true);
|
const isRouterShow = ref(true);
|
||||||
@ -15,7 +26,7 @@ provide('refresh', refreshMainPage);
|
|||||||
<template>
|
<template>
|
||||||
<el-main class="layout-main">
|
<el-main class="layout-main">
|
||||||
<router-view v-slot="{ Component, route }">
|
<router-view v-slot="{ Component, route }">
|
||||||
<transition :name="designStore.pageAnimateType" mode="out-in" appear>
|
<transition :name="transitionName" mode="out-in" appear>
|
||||||
<keep-alive :max="10" :include="keepAliveStore.keepAliveName">
|
<keep-alive :max="10" :include="keepAliveStore.keepAliveName">
|
||||||
<component :is="Component" v-if="isRouterShow" :key="route.fullPath" />
|
<component :is="Component" v-if="isRouterShow" :key="route.fullPath" />
|
||||||
</keep-alive>
|
</keep-alive>
|
||||||
@ -24,4 +35,52 @@ provide('refresh', refreshMainPage);
|
|||||||
</el-main>
|
</el-main>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<style scoped lang="scss">
|
||||||
|
.layout-main {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 默认聊天页面:上下滑动动画 */
|
||||||
|
.slide-enter-from {
|
||||||
|
margin-top: 200px;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
.slide-enter-active,
|
||||||
|
.slide-leave-active {
|
||||||
|
transition: all 0.3s; /* 缓出动画 */
|
||||||
|
}
|
||||||
|
.slide-enter-to {
|
||||||
|
margin-top: 0;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.slide-leave-from {
|
||||||
|
margin-top: 0;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.slide-leave-to {
|
||||||
|
margin-top: 200px;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 带id聊天页面:中间缩放动画 */
|
||||||
|
.zoom-fade-enter-from {
|
||||||
|
transform: scale(0.8); /* 进入前:缩小隐藏 */
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
.zoom-fade-enter-active,
|
||||||
|
.zoom-fade-leave-active {
|
||||||
|
transition: all 0.3s; /* 缓入动画 */
|
||||||
|
}
|
||||||
|
.zoom-fade-enter-to {
|
||||||
|
transform: scale(1); /* 进入后:正常大小 */
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.zoom-fade-leave-from {
|
||||||
|
transform: scale(1); /* 离开前:正常大小 */
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.zoom-fade-leave-to {
|
||||||
|
transform: scale(0.8); /* 离开后:缩小隐藏 */
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@ -5,7 +5,9 @@ import ChatWithId from '@/pages/chat/layouts/chatWithId/index.vue';
|
|||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|
||||||
const sessionId = computed(() => Number(route.params?.id));
|
const sessionId = computed(() => route.params?.id);
|
||||||
|
|
||||||
|
console.log(sessionId.value);
|
||||||
|
|
||||||
// const a = JSON.parse(`{"id":"chatcmpl-BVD1f4snw4KHOCKIgu4JOMoZbOwRh","object":"chat.completion.chunk","created":1746777939,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_ded0d14823","choices":[{"delta":{"content":"","role":"assistant"},"logprobs":null,"finish_reason":null,"index":0}],"usage":null}`)
|
// const a = JSON.parse(`{"id":"chatcmpl-BVD1f4snw4KHOCKIgu4JOMoZbOwRh","object":"chat.completion.chunk","created":1746777939,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_ded0d14823","choices":[{"delta":{"content":"","role":"assistant"},"logprobs":null,"finish_reason":null,"index":0}],"usage":null}`)
|
||||||
// console.log(a);
|
// console.log(a);
|
||||||
|
|||||||
@ -13,6 +13,7 @@ const senderValue = ref('');
|
|||||||
const isDeepThinking = computed(() => chatStore.isDeepThinking);
|
const isDeepThinking = computed(() => chatStore.isDeepThinking);
|
||||||
|
|
||||||
async function handleSend() {
|
async function handleSend() {
|
||||||
|
localStorage.setItem('chatContent', senderValue.value);
|
||||||
await sessionStore.createSessionList({
|
await sessionStore.createSessionList({
|
||||||
userId: userStore.userInfo?.userId as number,
|
userId: userStore.userInfo?.userId as number,
|
||||||
sessionContent: senderValue.value,
|
sessionContent: senderValue.value,
|
||||||
|
|||||||
@ -14,6 +14,7 @@ export const layoutRouter: RouteRecordRaw[] = [
|
|||||||
component: () => import('@/pages/chat/index.vue'),
|
component: () => import('@/pages/chat/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: '通用聊天页面',
|
title: '通用聊天页面',
|
||||||
|
isDefaultChat: true,
|
||||||
icon: 'HomeFilled',
|
icon: 'HomeFilled',
|
||||||
isHide: '1',
|
isHide: '1',
|
||||||
isKeepAlive: '0', // 是否缓存路由数据[0是,1否]
|
isKeepAlive: '0', // 是否缓存路由数据[0是,1否]
|
||||||
@ -27,6 +28,7 @@ export const layoutRouter: RouteRecordRaw[] = [
|
|||||||
component: () => import('@/pages/chat/index.vue'),
|
component: () => import('@/pages/chat/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: '带 ID 的聊天页面',
|
title: '带 ID 的聊天页面',
|
||||||
|
isDefaultChat: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@ -13,6 +13,9 @@ export const useChatStore = defineStore('chat', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const requestChatList = async (sessionId: number) => {
|
const requestChatList = async (sessionId: number) => {
|
||||||
|
// 如果没有 token 则不查询聊天记录
|
||||||
|
if (!userStore.token)
|
||||||
|
return;
|
||||||
try {
|
try {
|
||||||
const res = await getChatList({
|
const res = await getChatList({
|
||||||
sessionId,
|
sessionId,
|
||||||
|
|||||||
@ -26,7 +26,7 @@ export const useSessionStore = defineStore('session', () => {
|
|||||||
// 会话列表核心状态
|
// 会话列表核心状态
|
||||||
const sessionList = ref<ChatSessionVo[]>([]); // 会话数据列表
|
const sessionList = ref<ChatSessionVo[]>([]); // 会话数据列表
|
||||||
const currentPage = ref(1); // 当前页码(从1开始)
|
const currentPage = ref(1); // 当前页码(从1开始)
|
||||||
const pageSize = ref(20); // 每页显示数量
|
const pageSize = ref(25); // 每页显示数量
|
||||||
const hasMore = ref(true); // 是否还有更多数据
|
const hasMore = ref(true); // 是否还有更多数据
|
||||||
const isLoading = ref(false); // 全局加载状态(初始加载/刷新)
|
const isLoading = ref(false); // 全局加载状态(初始加载/刷新)
|
||||||
const isLoadingMore = ref(false); // 加载更多状态(区分初始加载)
|
const isLoadingMore = ref(false); // 加载更多状态(区分初始加载)
|
||||||
@ -45,6 +45,12 @@ export const useSessionStore = defineStore('session', () => {
|
|||||||
|
|
||||||
// 获取会话列表(核心分页方法)
|
// 获取会话列表(核心分页方法)
|
||||||
const requestSessionList = async (page: number = currentPage.value, force: boolean = false) => {
|
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))
|
if (!force && ((page > 1 && !hasMore.value) || isLoading.value || isLoadingMore.value))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -62,13 +68,11 @@ export const useSessionStore = defineStore('session', () => {
|
|||||||
|
|
||||||
const resArr = await get_session_list(params);
|
const resArr = await get_session_list(params);
|
||||||
|
|
||||||
// 预处理会话分组
|
// 预处理会话分组 并添加前缀图标
|
||||||
const res = processSessions(resArr.rows);
|
const res = processSessions(resArr.rows);
|
||||||
|
|
||||||
const allSessions = new Map(sessionList.value.map(item => [item.id, item])); // 现有所有数据
|
const allSessions = new Map(sessionList.value.map(item => [item.id, item])); // 现有所有数据
|
||||||
res.forEach(item =>
|
res.forEach(item => allSessions.set(item.id, { ...item })); // 更新/添加数据
|
||||||
allSessions.set(item.id, { ...item, prefixIcon: markRaw(ChatLineRound) }),
|
|
||||||
); // 更新/添加数据
|
|
||||||
|
|
||||||
// 按服务端排序重建列表(假设分页数据是按时间倒序,第一页是最新,后续页依次递减)
|
// 按服务端排序重建列表(假设分页数据是按时间倒序,第一页是最新,后续页依次递减)
|
||||||
// 此处需根据接口返回的排序规则调整,假设每页数据是递增的(第一页最新,第二页次新,第三页 oldest)
|
// 此处需根据接口返回的排序规则调整,假设每页数据是递增的(第一页最新,第二页次新,第三页 oldest)
|
||||||
@ -104,6 +108,16 @@ export const useSessionStore = defineStore('session', () => {
|
|||||||
|
|
||||||
// 发送消息后创建新会话
|
// 发送消息后创建新会话
|
||||||
const createSessionList = async (data: Omit<CreateSessionDTO, 'id'>) => {
|
const createSessionList = async (data: Omit<CreateSessionDTO, 'id'>) => {
|
||||||
|
if (!userStore.token) {
|
||||||
|
router.replace({
|
||||||
|
name: 'chatWithId',
|
||||||
|
params: {
|
||||||
|
id: 'not_login',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await create_session(data);
|
const res = await create_session(data);
|
||||||
// 创建会话后立刻查询列表会话
|
// 创建会话后立刻查询列表会话
|
||||||
@ -202,6 +216,7 @@ export const useSessionStore = defineStore('session', () => {
|
|||||||
return {
|
return {
|
||||||
...session,
|
...session,
|
||||||
group, // 新增分组键字段
|
group, // 新增分组键字段
|
||||||
|
prefixIcon: markRaw(ChatLineRound), // 图标为静态组件,使用 markRaw 标记为静态组件
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user