-
+
+
+
+
+
diff --git a/src/pages/chat/layouts/chatDefaul/index.vue b/src/pages/chat/layouts/chatDefaul/index.vue
new file mode 100644
index 0000000..fbfe6c7
--- /dev/null
+++ b/src/pages/chat/layouts/chatDefaul/index.vue
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
diff --git a/src/pages/chat/layouts/chatWithId/index.vue b/src/pages/chat/layouts/chatWithId/index.vue
new file mode 100644
index 0000000..74fbcdd
--- /dev/null
+++ b/src/pages/chat/layouts/chatWithId/index.vue
@@ -0,0 +1,18 @@
+
+
+
+
+ 新建对话
+
+
+
diff --git a/src/pages/error/403.vue b/src/pages/error/403.vue
new file mode 100644
index 0000000..7fa5887
--- /dev/null
+++ b/src/pages/error/403.vue
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+ 403
+
+
+ 对不起,您没有权限访问
+
+
+
+ 返回首页
+
+
+
+
+
+
diff --git a/src/pages/error/404.vue b/src/pages/error/404.vue
new file mode 100644
index 0000000..2c45f72
--- /dev/null
+++ b/src/pages/error/404.vue
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+ 404
+
+
+ 您想看的页面不存在哟
+
+
+
+ 返回首页
+
+
+
+
+
+
diff --git a/src/pages/error/500.vue b/src/pages/error/500.vue
new file mode 100644
index 0000000..bcd0368
--- /dev/null
+++ b/src/pages/error/500.vue
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+ 500
+
+
+ 服务器好像开小差了!请稍后试试...
+
+
+
+ 返回首页
+
+
+
+
+
+
diff --git a/src/pages/login/index.vue b/src/pages/login/index.vue
deleted file mode 100644
index e426694..0000000
--- a/src/pages/login/index.vue
+++ /dev/null
@@ -1,53 +0,0 @@
-
-
-
-
-
-
登录
-
-
-
-
-
-
-
-
-
- 登录
-
-
-
-
-
-
diff --git a/src/router/index.ts b/src/router/index.ts
deleted file mode 100644
index 7cc867e..0000000
--- a/src/router/index.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import type { RouteRecordRaw } from 'vue-router';
-import { createRouter, createWebHistory } from 'vue-router';
-import { jwtGuard } from './permissions';
-
-const routes: Readonly
[] = [
- {
- path: '/',
- redirect: 'chat',
- component: () => import('@/layouts/index.vue'),
- children: [
- {
- path: '/chat',
- name: 'chat',
- component: () => import('@/pages/chat/index.vue'),
- },
- {
- path: '/chat:id',
- name: 'chatWithoutId',
- component: () => import('@/pages/chat/index.vue'),
- },
- ],
- },
- {
- path: '/login',
- name: 'login',
- component: () => import('@/pages/login/index.vue'),
- },
-] as const;
-
-const router = createRouter({
- history: createWebHistory(),
- routes,
-});
-
-console.group('Routes');
-console.log('routes', router.getRoutes());
-console.groupEnd();
-
-router.beforeEach(jwtGuard());
-
-export default router;
diff --git a/src/router/permissions.ts b/src/router/permissions.ts
deleted file mode 100644
index 7a33a3e..0000000
--- a/src/router/permissions.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import type { NavigationGuard } from 'vue-router';
-
-export function jwtGuard(): NavigationGuard {
- return () => {
- console.log('进入路由守卫');
- };
-}
diff --git a/src/routers/index.ts b/src/routers/index.ts
new file mode 100644
index 0000000..7d0f06a
--- /dev/null
+++ b/src/routers/index.ts
@@ -0,0 +1,85 @@
+import type { NavigationGuardNext, RouteLocationNormalized } from 'vue-router';
+import { useNProgress } from '@vueuse/integrations/useNProgress';
+import { createRouter, createWebHistory } from 'vue-router';
+import { ROUTER_WHITE_LIST } from '@/config';
+import { errorRouter, layoutRouter, staticRouter } from '@/routers/modules/staticRouter';
+import { useUserStore } from '@/stores';
+
+const { start, done } = useNProgress(0, {
+ showSpinner: false,
+ trickleSpeed: 200,
+ minimum: 0.3,
+ easing: 'ease',
+ speed: 500,
+});
+
+const router = createRouter({
+ history: createWebHistory(),
+ routes: [...layoutRouter, ...staticRouter, ...errorRouter],
+ strict: false,
+ scrollBehavior: () => ({ left: 0, top: 0 }),
+});
+
+console.group('Routes');
+console.log('routes', router.getRoutes());
+console.groupEnd();
+
+// 路由前置守卫
+router.beforeEach(
+ async (
+ to: RouteLocationNormalized,
+ _from: RouteLocationNormalized,
+ next: NavigationGuardNext,
+ ) => {
+ const userStore = useUserStore();
+
+ // 1、NProgress 开始
+ start();
+
+ // 2、标题
+ document.title = to.meta.title || import.meta.env.VITE_WEB_TITLE;
+
+ // 3、权限 预留
+ // 3、判断是访问登陆页,有Token访问当前页面,token过期访问接口,axios封装则自动跳转登录页面,没有Token重置路由到登陆页。
+ // if (to.path.toLocaleLowerCase() === LOGIN_URL) {
+ // // 有Token访问当前页面
+ // if (userStore.token) {
+ // return next(from.fullPath);
+ // }
+ // else {
+ // ElMessage.error('账号身份已过期,请重新登录🌻');
+ // }
+ // // 没有Token重置路由到登陆页。
+ // // resetRouter(); // 预留
+ // return next();
+ // }
+
+ // 4、判断访问页面是否在路由白名单地址[静态路由]中,如果存在直接放行。
+ if (ROUTER_WHITE_LIST.includes(to.path))
+ return next();
+
+ // 5、判断是否有 Token,没有重定向到 login 页面。
+ if (!userStore.token)
+ userStore.logout();
+
+ // 其余逻辑 预留...
+
+ // 6、正常访问页面。
+ next();
+ },
+);
+
+// 路由跳转错误
+router.onError((error) => {
+ // 结束全屏动画
+ done();
+ console.warn('路由错误', error.message);
+});
+
+// 后置路由
+router.afterEach(() => {
+ // 结束全屏动画
+ done();
+});
+
+export default router;
diff --git a/src/routers/modules/dynamicRouter.ts b/src/routers/modules/dynamicRouter.ts
new file mode 100644
index 0000000..211c169
--- /dev/null
+++ b/src/routers/modules/dynamicRouter.ts
@@ -0,0 +1,39 @@
+// 预留
+import router from '@/routers/index';
+import { useUserStore } from '@/stores';
+import { useAuthStore } from '@/stores/modules/auth';
+
+export async function initDynamicRouter() {
+ const userStore = useUserStore();
+ const authStore = useAuthStore();
+
+ try {
+ // 1、预留: 获取菜单列表 || 按钮权限列表 || 递归菜单数据
+ await authStore.requestAuthMenuList();
+
+ // 2、判断当前用户是否拥有菜单权限
+ console.log('authStore.authMenuList', authStore.authMenuList);
+
+ if (authStore.authMenuList == null || authStore.authMenuList.length === 0) {
+ userStore.logout();
+ return;
+ }
+
+ // 3、添加动态路由
+ authStore.authMenuList.forEach((item: any) => {
+ if (item.isFull === '0') {
+ // 如果是全屏的话,直接为整个页面
+ router.addRoute(item);
+ }
+ else {
+ router.addRoute('layout', item);
+ }
+ });
+ }
+ catch (error) {
+ console.log(error);
+ // 当菜单请求出错时,重定向到首页
+ userStore.logout();
+ return Promise.reject(error);
+ }
+}
diff --git a/src/routers/modules/staticRouter.ts b/src/routers/modules/staticRouter.ts
new file mode 100644
index 0000000..a711e54
--- /dev/null
+++ b/src/routers/modules/staticRouter.ts
@@ -0,0 +1,91 @@
+import type { RouteRecordRaw } from 'vue-router';
+import { HOME_URL } from '@/config';
+
+// LayoutRouter[布局路由]
+export const layoutRouter: RouteRecordRaw[] = [
+ {
+ path: '/',
+ redirect: HOME_URL,
+ component: () => import('@/layouts/index.vue'),
+ children: [
+ {
+ path: HOME_URL,
+ name: 'chat',
+ component: () => import('@/pages/chat/index.vue'),
+ meta: {
+ title: '通用聊天页面',
+ icon: 'HomeFilled',
+ isHide: '1',
+ isKeepAlive: '0', // 是否缓存路由数据[0是,1否]
+ isFull: '1', // 是否缓存全屏[0是,1否]
+ // enName: "Master Station", // 英文名称 预留
+ },
+ },
+ {
+ path: '/chat/:id',
+ name: 'chatWithId',
+ component: () => import('@/pages/chat/index.vue'),
+ meta: {
+ title: '带 ID 的聊天页面',
+ },
+ },
+ ],
+ },
+];
+
+// staticRouter[静态路由] 预留
+export const staticRouter: RouteRecordRaw[] = [];
+
+// errorRouter (错误页面路由)
+export const errorRouter = [
+ {
+ path: '/403',
+ name: '403',
+ component: () => import('@/pages/error/403.vue'),
+ meta: {
+ title: '403页面',
+ enName: '403 Page', // 英文名称
+ icon: 'QuestionFilled', // 菜单图标
+ isHide: '1', // 代表路由在菜单中是否隐藏,是否隐藏[0隐藏,1显示]
+ isLink: '1', // 是否外链[有值则是外链]
+ isKeepAlive: '0', // 是否缓存路由数据[0是,1否]
+ isFull: '1', // 是否缓存全屏[0是,1否]
+ isAffix: '1', // 是否缓存固定路由[0是,1否]
+ },
+ },
+ {
+ path: '/404',
+ name: '404',
+ component: () => import('@/pages/error/404.vue'),
+ meta: {
+ title: '404页面',
+ enName: '404 Page', // 英文名称
+ icon: 'CircleCloseFilled', // 菜单图标
+ isHide: '1', // 代表路由在菜单中是否隐藏,是否隐藏[0隐藏,1显示]
+ isLink: '1', // 是否外链[有值则是外链]
+ isKeepAlive: '0', // 是否缓存路由数据[0是,1否]
+ isFull: '1', // 是否缓存全屏[0是,1否]
+ isAffix: '1', // 是否缓存固定路由[0是,1否]
+ },
+ },
+ {
+ path: '/500',
+ name: '500',
+ component: () => import('@/pages/error/500.vue'),
+ meta: {
+ title: '500页面',
+ enName: '500 Page', // 英文名称
+ icon: 'WarningFilled', // 图标
+ isHide: '1', // 代表路由在菜单中是否隐藏,是否隐藏[0隐藏,1显示]
+ isLink: '1', // 是否外链[有值则是外链]
+ isKeepAlive: '0', // 是否缓存路由数据[0是,1否]
+ isFull: '1', // 是否缓存全屏[0是,1否]
+ isAffix: '1', // 是否缓存固定路由[0是,1否]
+ },
+ },
+ // 找不到path将跳转404页面
+ {
+ path: '/:pathMatch(.*)*',
+ component: () => import('@/pages/error/404.vue'),
+ },
+];
diff --git a/src/router/types.ts b/src/routers/types.ts
similarity index 100%
rename from src/router/types.ts
rename to src/routers/types.ts
diff --git a/src/store/index.ts b/src/stores/index.ts
similarity index 100%
rename from src/store/index.ts
rename to src/stores/index.ts
diff --git a/src/stores/modules/auth.ts b/src/stores/modules/auth.ts
new file mode 100644
index 0000000..de13517
--- /dev/null
+++ b/src/stores/modules/auth.ts
@@ -0,0 +1,18 @@
+import { defineStore } from 'pinia';
+
+// 权限状态管理
+export const useAuthStore = defineStore('auth', () => {
+ // 权限菜单列表
+ const authMenuList = ref([]);
+
+ // 请求权限菜单列表
+ const requestAuthMenuList = async () => {
+ // const res = await initDynamicRouter();
+ authMenuList.value = [];
+ };
+
+ return {
+ authMenuList,
+ requestAuthMenuList,
+ };
+});
diff --git a/src/store/modules/chat.ts b/src/stores/modules/chat.ts
similarity index 79%
rename from src/store/modules/chat.ts
rename to src/stores/modules/chat.ts
index fef7600..a68590b 100644
--- a/src/store/modules/chat.ts
+++ b/src/stores/modules/chat.ts
@@ -5,6 +5,7 @@ import { useUserStore } from './user';
export const useChatStore = defineStore('chat', () => {
const userStore = useUserStore();
+
const chatMap = ref>({});
const setChatMap = (id: number, data: ChatMessageVo[]) => {
@@ -26,8 +27,17 @@ export const useChatStore = defineStore('chat', () => {
}
};
+ // 是否开启深度思考
+ const isDeepThinking = ref(false);
+
+ const setDeepThinking = (value: boolean) => {
+ isDeepThinking.value = value;
+ };
+
return {
chatMap,
requestChatList,
+ isDeepThinking,
+ setDeepThinking,
};
});
diff --git a/src/store/modules/design.ts b/src/stores/modules/design.ts
similarity index 100%
rename from src/store/modules/design.ts
rename to src/stores/modules/design.ts
diff --git a/src/store/modules/keepAlive.ts b/src/stores/modules/keepAlive.ts
similarity index 100%
rename from src/store/modules/keepAlive.ts
rename to src/stores/modules/keepAlive.ts
diff --git a/src/stores/modules/session.ts b/src/stores/modules/session.ts
new file mode 100644
index 0000000..81e5e84
--- /dev/null
+++ b/src/stores/modules/session.ts
@@ -0,0 +1,69 @@
+import type { CreateSessionDTO } from '@/api/session/types';
+import { defineStore } from 'pinia';
+import { useRouter } from 'vue-router';
+import { createSession, getSessionList } from '@/api/session';
+import { useUserStore } from './user';
+
+export const useSessionStore = defineStore('session', () => {
+ const router = useRouter();
+ const userStore = useUserStore();
+
+ const sessionList = ref([]);
+
+ // 单独点击创建新对话按钮
+ const createSessionBtn = async () => {
+ try {
+ // 直接回到首页
+ router.replace({ name: 'chat' });
+ }
+ catch (error) {
+ console.error('createSessionBtn:', error);
+ }
+ };
+
+ // 发送消息后,创建对话
+ const createSessionList = async (data: CreateSessionDTO) => {
+ try {
+ const res = await createSession(data);
+ localStorage.setItem('chatContent', data.sessionContent);
+ router.replace({
+ name: 'chatWithId',
+ params: {
+ id: `${res.data}`,
+ },
+ });
+ }
+ catch (error) {
+ console.error('createSession:', error);
+ }
+ };
+
+ // 获取会话列表
+ const requestSessionList = async () => {
+ try {
+ const res = await getSessionList({
+ userId: userStore.userInfo?.userId as number,
+ });
+ if (res.rows) {
+ sessionList.value = (res.rows || []).map(item => ({
+ ...item,
+ // 提供默认值
+ remark: item.remark || '',
+ sessionContent: item.sessionContent || '',
+ sessionTitle: item.sessionTitle || '',
+ userId: item.userId || -1,
+ }));
+ }
+ }
+ catch (error) {
+ console.error('getSessionList:', error);
+ }
+ };
+
+ return {
+ sessionList,
+ createSessionBtn,
+ createSessionList,
+ requestSessionList,
+ };
+});
diff --git a/src/store/modules/user.ts b/src/stores/modules/user.ts
similarity index 100%
rename from src/store/modules/user.ts
rename to src/stores/modules/user.ts
diff --git a/src/styles/var.scss b/src/styles/var.scss
index 34c2e99..46df652 100644
--- a/src/styles/var.scss
+++ b/src/styles/var.scss
@@ -16,4 +16,9 @@
--login-dialog-logo-background: #fff;
--login-dialog-logo-text-color: #191919;
+
+
+ /* 覆盖 element-plus 样式 */
+ --el-border-radius-base: 12px;
+ --el-messagebox-border-radius: 16px;
}
diff --git a/src/utils/request.ts b/src/utils/request.ts
index 2f96764..f88a0aa 100644
--- a/src/utils/request.ts
+++ b/src/utils/request.ts
@@ -2,7 +2,7 @@ import type { HookFetchPlugin } from 'hook-fetch';
import { ElMessage } from 'element-plus';
import hookFetch from 'hook-fetch';
import { sseTextDecoderPlugin } from 'hook-fetch/plugins';
-import { useUserStore } from '@/store';
+import { useUserStore } from '@/stores';
interface BaseResponse {
code: number;
@@ -34,8 +34,9 @@ function jwtPlugin(): HookFetchPlugin {
return response;
}
if (response.result?.code === 401) {
- // 如果没有权限,也不退出,则是弹框提示登录
+ // 如果没有权限,退出,且弹框提示登录
userStore.logout();
+ userStore.openLoginDialog();
}
ElMessage.error(response.result?.msg);
return Promise.reject(response);