diff --git a/frontend/src/layouts/LayoutTransverse/index.vue b/frontend/src/layouts/LayoutTransverse/index.vue
index 447ac20..81e5af4 100644
--- a/frontend/src/layouts/LayoutTransverse/index.vue
+++ b/frontend/src/layouts/LayoutTransverse/index.vue
@@ -61,9 +61,12 @@ const menuList = computed(() => authStore.showMenuListGet)
const showMenuFlag = computed(() => authStore.showMenuFlagGet)
const activeMenu = computed(() => (route.meta.activeMenu ? route.meta.activeMenu : route.path) as string)
-const handleClickMenu = (subItem: Menu.MenuOptions) => {
- if (subItem.meta.isLink) return window.open(subItem.meta.isLink, '_blank')
- router.push(subItem.path)
+const handleClickMenu = async (subItem: Menu.MenuOptions) => {
+ if (subItem.meta?.isLink) {
+ window.open(subItem.meta.isLink, '_blank')
+ return
+ }
+ await router.push(subItem.path)
}
diff --git a/frontend/src/layouts/components/Footer/index.vue b/frontend/src/layouts/components/Footer/index.vue
index 0f4e5fd..67acae0 100644
--- a/frontend/src/layouts/components/Footer/index.vue
+++ b/frontend/src/layouts/components/Footer/index.vue
@@ -28,8 +28,14 @@
import { computed } from 'vue'
import { useAuthStore } from '@/stores/modules/auth'
import { useModeStore } from '@/stores/modules/mode' // 引入模式 store
+import { useTabsStore } from '@/stores/modules/tabs'
+import { useRouter } from 'vue-router'
+import { initDynamicRouter } from '@/routers/modules/dynamicRouter'
+
+const router = useRouter()
const authStore = useAuthStore()
const modeStore = useModeStore()
+const tabsStore = useTabsStore()
const title = computed(() => {
return modeStore.currentMode === '' ? '选择模块' : modeStore.currentMode + '模块'
@@ -64,7 +70,9 @@ const handelOpen = async (item: string, key: string) => {
await authStore.setShowMenu()
modeStore.setCurrentMode(item) // 将模式code存入 store
// 强制刷新页面
- window.location.reload()
+ await tabsStore.closeMultipleTab()
+ await initDynamicRouter()
+ await router.push({ path: '/home/index' })
}
diff --git a/frontend/src/routers/index.ts b/frontend/src/routers/index.ts
index e0968a3..0a625a9 100644
--- a/frontend/src/routers/index.ts
+++ b/frontend/src/routers/index.ts
@@ -6,6 +6,9 @@ import { initDynamicRouter } from '@/routers/modules/dynamicRouter'
import { staticRouter } from '@/routers/modules/staticRouter'
import NProgress from '@/config/nprogress'
+// 白名单转换为 Set 提高性能
+const WHITE_LIST_SET = new Set(ROUTER_WHITE_LIST)
+
const mode = import.meta.env.VITE_ROUTER_MODE
const routerMode = {
@@ -30,11 +33,9 @@ const routerMode = {
* @param meta.isKeepAlive ==> 当前路由是否缓存
* */
const router = createRouter({
- history: routerMode[mode](),
+ history: routerMode[mode]?.() || createWebHashHistory(), // 默认 fallback 到 hash 模式
routes: [...staticRouter],
- // 不区分路由大小写,非严格模式下提供了更宽松的路径匹配
strict: false,
- // 页面刷新时,滚动条位置还原
scrollBehavior: () => ({ left: 0, top: 0 })
})
@@ -44,38 +45,52 @@ const router = createRouter({
router.beforeEach(async (to, from, next) => {
const userStore = useUserStore()
const authStore = useAuthStore()
+
// 1.NProgress 开始
NProgress.start()
+
// 2.动态设置标题
const title = import.meta.env.VITE_GLOB_APP_TITLE
document.title = to.meta.title ? `${to.meta.title} - ${title}` : title
- // 3.判断是访问登陆页,有 Token 就在当前页面,没有 Token 重置路由到登陆页
+ // 3.判断是访问登陆页,有 Token 就留在当前页,没有 Token 重置路由到登陆页
if (to.path.toLocaleLowerCase() === LOGIN_URL) {
- if (userStore.accessToken) return next(from.fullPath)
+ if (userStore.accessToken) {
+ // 已登录则不再回到登录页,直接跳过
+ return next('/')
+ }
resetRouter()
return next()
}
// 4.判断访问页面是否在路由白名单地址(静态路由)中,如果存在直接放行
- if (ROUTER_WHITE_LIST.includes(to.path)) return next()
+ if (WHITE_LIST_SET.has(to.path)) return next()
// 5.判断是否有 Token,没有重定向到 login 页面
if (!userStore.accessToken) return next({ path: LOGIN_URL, replace: true })
// 6.如果没有菜单列表,就重新请求菜单列表并添加动态路由
if (!authStore.authMenuListGet.length) {
- await initDynamicRouter()
- return next({ ...to, replace: true })
+ try {
+ await initDynamicRouter()
+ return next({ ...to, replace: true })
+ } catch (error) {
+ console.error('动态路由加载失败:', error)
+ // 清除 token 并跳转登录页
+ await userStore.logout()
+ return next({ path: LOGIN_URL, replace: true })
+ }
}
// 7.存储 routerName 做按钮权限筛选
await authStore.setRouteName(to.name as string)
+
// 8. 当前页面是否有激活信息,没有就刷新
const activateInfo = authStore.activateInfo
if (!Object.keys(activateInfo).length) {
await authStore.setActivateInfo()
}
+
// 9.正常访问页面
next()
})
@@ -96,7 +111,7 @@ export const resetRouter = () => {
* */
router.onError(error => {
NProgress.done()
- //console.warn('路由错误', error.message)
+ console.warn('路由错误', error.message)
})
/**
diff --git a/frontend/src/routers/modules/dynamicRouter.ts b/frontend/src/routers/modules/dynamicRouter.ts
index cb43751..b2cf26d 100644
--- a/frontend/src/routers/modules/dynamicRouter.ts
+++ b/frontend/src/routers/modules/dynamicRouter.ts
@@ -1,60 +1,117 @@
-import router from "@/routers/index";
-import { LOGIN_URL } from "@/config";
-import { RouteRecordRaw } from "vue-router";
-import { ElNotification } from "element-plus";
-import { useUserStore } from "@/stores/modules/user";
-import { useAuthStore } from "@/stores/modules/auth";
+import router from '@/routers'
+import { LOGIN_URL } from '@/config'
+import { type RouteRecordRaw } from 'vue-router'
+import { ElNotification } from 'element-plus'
+import { useUserStore } from '@/stores/modules/user'
+import { useAuthStore } from '@/stores/modules/auth'
// 引入 views 文件夹下所有 vue 文件
-const modules = import.meta.glob("@/views/**/*.vue");
+const modules = import.meta.glob('@/views/**/*.vue')
+
+let isInitializing = false
+
+/**
+ * 清除已有的动态路由
+ */
+const clearDynamicRoutes = () => {
+ const routes = router.getRoutes()
+ routes.forEach(route => {
+ if (route.name && route.name !== 'layout' && route.name !== 'login') {
+ router.removeRoute(route.name)
+ }
+ })
+}
+
+/**
+ * 根据 component 路径查找对应的模块
+ * @param path 组件路径
+ */
+const resolveComponentModule = async (path: string) => {
+ // 规范化路径,去除首尾斜杠
+ let normalizedPath = path.trim()
+ if (!normalizedPath.startsWith('/')) {
+ normalizedPath = '/' + normalizedPath
+ }
+ if (normalizedPath.endsWith('.vue')) {
+ normalizedPath = normalizedPath.slice(0, -4)
+ }
+
+ const fullPath = `/src/views${normalizedPath}.vue`
+ return modules[fullPath]
+}
/**
* @description 初始化动态路由
*/
export const initDynamicRouter = async () => {
- const userStore = useUserStore();
- const authStore = useAuthStore();
+ if (isInitializing) return Promise.reject(new Error('Dynamic router initialization in progress'))
- try {
- // 1.获取菜单列表 && 按钮权限列表
- await authStore.getAuthMenuList();
- await authStore.getAuthButtonList();
+ isInitializing = true
+ const userStore = useUserStore()
+ const authStore = useAuthStore()
- // 2.判断当前用户有没有菜单权限
- if (!authStore.authMenuListGet.length) {
- ElNotification({
- title: "无权限访问",
- message: "当前账号无任何菜单权限,请联系系统管理员!",
- type: "warning",
- duration: 3000
- });
- userStore.setAccessToken("");
- userStore.setRefreshToken("");
- userStore.setExp(0)
- router.replace(LOGIN_URL);
- return Promise.reject("No permission");
+ try {
+ // 1. 获取菜单列表 && 按钮权限列表
+ await authStore.getAuthMenuList()
+ await authStore.getAuthButtonList()
+
+ // 2. 判断当前用户有没有菜单权限
+ if (!authStore.authMenuListGet.length) {
+ ElNotification({
+ title: '无权限访问',
+ message: '当前账号无任何菜单权限,请联系系统管理员!',
+ type: 'warning',
+ duration: 3000
+ })
+ userStore.setAccessToken('')
+ userStore.setRefreshToken('')
+ userStore.setExp(0)
+ await router.replace(LOGIN_URL)
+ return Promise.reject('No permission')
+ }
+
+ // 3. 清理之前的动态路由
+ clearDynamicRoutes()
+
+ // 4. 添加动态路由
+ for (const item of authStore.flatMenuListGet) {
+ // 删除 children 避免冗余嵌套
+ if (item.children) delete item.children
+
+ // 处理组件映射
+ if (item.component && typeof item.component === 'string') {
+ const moduleLoader = await resolveComponentModule(item.component)
+ if (moduleLoader) {
+ item.component = moduleLoader
+ } else {
+ console.warn(`未能找到组件: ${item.component}`)
+ continue
+ }
+ }
+
+ // 类型守卫:确保满足 RouteRecordRaw 接口要求
+ if (
+ typeof item.path === 'string' &&
+ (typeof item.component === 'function' || typeof item.redirect === 'string')
+ ) {
+ const routeItem = item as unknown as RouteRecordRaw
+ if (item.meta.isFull) {
+ router.addRoute(routeItem)
+ } else {
+ router.addRoute('layout', routeItem)
+ }
+ } else {
+ console.warn('Invalid route item:', item)
+ }
+ }
+ } catch (error) {
+ // 当按钮 || 菜单请求出错时,重定向到登陆页
+ userStore.setAccessToken('')
+ userStore.setRefreshToken('')
+ userStore.setExp(0)
+ await router.replace(LOGIN_URL)
+ return Promise.reject(error)
+ } finally {
+ isInitializing = false
}
-
- // 3.添加动态路由
- authStore.flatMenuListGet.forEach(item => {
- item.children && delete item.children;
-
- if (item.component && typeof item.component == "string") {
- item.component = modules["/src/views" + item.component + ".vue"];
- }
-
- if (item.meta.isFull) {
- router.addRoute(item as unknown as RouteRecordRaw);
- } else {
- router.addRoute("layout", item as unknown as RouteRecordRaw);
- }
- });
- } catch (error) {
- // 当按钮 || 菜单请求出错时,重定向到登陆页
- userStore.setAccessToken("");
- userStore.setRefreshToken("");
- userStore.setExp(0)
- router.replace(LOGIN_URL);
- return Promise.reject(error);
- }
-};
+}
diff --git a/frontend/src/stores/interface/index.ts b/frontend/src/stores/interface/index.ts
index 64b82e9..b548d7f 100644
--- a/frontend/src/stores/interface/index.ts
+++ b/frontend/src/stores/interface/index.ts
@@ -1,65 +1,69 @@
-export type LayoutType = 'vertical' | 'classic' | 'transverse' | 'columns';
+import { type Activate } from '@/api/activate/interface'
-export type AssemblySizeType = 'large' | 'default' | 'small';
+export type LayoutType = 'vertical' | 'classic' | 'transverse' | 'columns'
-export type LanguageType = 'zh' | 'en' | null;
+export type AssemblySizeType = 'large' | 'default' | 'small'
+
+export type LanguageType = 'zh' | 'en' | null
/* GlobalState */
export interface GlobalState {
- layout: LayoutType;
- assemblySize: AssemblySizeType;
- language: LanguageType;
- maximize: boolean;
- primary: string;
- isDark: boolean;
- isGrey: boolean;
- isWeak: boolean;
- asideInverted: boolean;
- headerInverted: boolean;
- isCollapse: boolean;
- accordion: boolean;
- breadcrumb: boolean;
- breadcrumbIcon: boolean;
- tabs: boolean;
- tabsIcon: boolean;
- footer: boolean;
+ layout: LayoutType
+ assemblySize: AssemblySizeType
+ language: LanguageType
+ maximize: boolean
+ primary: string
+ isDark: boolean
+ isGrey: boolean
+ isWeak: boolean
+ asideInverted: boolean
+ headerInverted: boolean
+ isCollapse: boolean
+ accordion: boolean
+ breadcrumb: boolean
+ breadcrumbIcon: boolean
+ tabs: boolean
+ tabsIcon: boolean
+ footer: boolean
}
/* UserState */
export interface UserState {
- accessToken: string;
- refreshToken: string;
- isRefreshToken: boolean;
- userInfo: { id: string, name: string,loginName:string };
+ accessToken: string
+ refreshToken: string
+ isRefreshToken: boolean
+ exp: number
+ userInfo: { id: string; name: string; loginName: string }
}
/* tabsMenuProps */
export interface TabsMenuProps {
- icon: string;
- title: string;
- path: string;
- name: string;
- close: boolean;
- isKeepAlive: boolean;
- unshift?: boolean;
+ icon: string
+ title: string
+ path: string
+ name: string
+ close: boolean
+ isKeepAlive: boolean
+ unshift?: boolean
}
/* TabsState */
export interface TabsState {
- tabsMenuList: TabsMenuProps[];
+ tabsMenuList: TabsMenuProps[]
}
/* AuthState */
export interface AuthState {
- routeName: string;
+ routeName: string
authButtonList: {
- [key: string]: string[];
- };
- authMenuList: Menu.MenuOptions[];
- showMenuFlag: boolean;
+ [key: string]: string[]
+ }
+ authMenuList: Menu.MenuOptions[]
+ showMenuFlag: boolean
+ activateInfo: Activate.ActivationCodePlaintext
}
/* KeepAliveState */
export interface KeepAliveState {
- keepAliveName: string[];
+ keepAliveName: string[]
}
diff --git a/frontend/src/stores/modules/auth.ts b/frontend/src/stores/modules/auth.ts
index e438136..5e99f51 100644
--- a/frontend/src/stores/modules/auth.ts
+++ b/frontend/src/stores/modules/auth.ts
@@ -1,15 +1,13 @@
import { defineStore } from 'pinia'
-import { AuthState } from '@/stores/interface'
+import { type AuthState } from '@/stores/interface'
import { getAuthButtonListApi, getAuthMenuListApi } from '@/api/user/login'
import { getAllBreadcrumbList, getFlatMenuList, getShowMenuList } from '@/utils'
-import { useRouter } from 'vue-router'
import { AUTH_STORE_KEY } from '@/stores/constant'
import { useModeStore } from '@/stores/modules/mode'
import { getLicense } from '@/api/activate'
import type { Activate } from '@/api/activate/interface'
-export const useAuthStore = defineStore({
- id: AUTH_STORE_KEY,
+export const useAuthStore = defineStore(AUTH_STORE_KEY, {
state: (): AuthState => ({
// 按钮权限列表
authButtonList: {},
@@ -19,8 +17,7 @@ export const useAuthStore = defineStore({
routeName: '',
//登录不显示菜单栏和导航栏,点击进入测试的时候显示
showMenuFlag: JSON.parse(localStorage.getItem('showMenuFlag') as string),
- router: useRouter(),
- activateInfo: {}
+ activateInfo: {} as Activate.ActivationCodePlaintext
}),
getters: {
// 按钮权限列表
@@ -72,10 +69,9 @@ export const useAuthStore = defineStore({
localStorage.setItem('showMenuFlag', 'true')
},
//更改模式
- async changeModel() {
+ changeModel() {
this.showMenuFlag = false
localStorage.removeItem('showMenuFlag')
- this.router.push({ path: '/home/index' })
},
async setActivateInfo() {
const license_result = await getLicense()
diff --git a/frontend/src/stores/modules/check.ts b/frontend/src/stores/modules/check.ts
index 4f30b1e..8a7b721 100644
--- a/frontend/src/stores/modules/check.ts
+++ b/frontend/src/stores/modules/check.ts
@@ -1,32 +1,37 @@
-import {defineStore} from "pinia";
-import {CHECK_STORE_KEY} from "@/stores/constant";
-import type {CheckData} from "@/api/check/interface";
-import type {Plan} from '@/api/plan/interface'
-import {useAppSceneStore} from "@/stores/modules/mode";
+import { defineStore } from 'pinia'
+import { CHECK_STORE_KEY } from '@/stores/constant'
+import type { CheckData } from '@/api/check/interface'
+import type { Plan } from '@/api/plan/interface'
+import { useAppSceneStore } from '@/stores/modules/mode'
export const useCheckStore = defineStore(CHECK_STORE_KEY, {
state: () => ({
devices: [] as CheckData.Device[],
plan: {} as Plan.ResPlan,
- selectTestItems: {preTest: true, timeTest: false, channelsTest: false, test: true} as CheckData.SelectTestItem,
+ selectTestItems: {
+ preTest: true,
+ timeTest: false,
+ channelsTest: false,
+ test: true
+ } as CheckData.SelectTestItem,
checkType: 1, // 0:手动检测 1:自动检测
reCheckType: 1, // 0:不合格项复检 1:全部复检
showDetailType: 0, // 0:数据查询 1:误差体系跟换 2:正式检测
temperature: 0,
humidity: 0,
- chnNumList: [],//连线数据
- nodesConnectable: true,//设置是能可以连线
+ chnNumList: [] as string[], //连线数据
+ nodesConnectable: true //设置是能可以连线
}),
getters: {},
actions: {
addDevices(device: CheckData.Device[]) {
- this.devices.push(...device);
+ this.devices.push(...device)
},
setPlan(plan: Plan.ResPlan) {
this.plan = plan
},
clearDevices() {
- this.devices = [];
+ this.devices = []
},
initSelectTestItems() {
const appSceneStore = useAppSceneStore()
@@ -56,12 +61,11 @@ export const useCheckStore = defineStore(CHECK_STORE_KEY, {
setHumidity(humidity: number) {
this.humidity = humidity
},
- setChnNum(chnNumList: string[]) {
+ setChnNum(chnNumList: string[]) {
this.chnNumList = chnNumList
},
- setNodesConnectable(nodesConnectable: boolean) {
+ setNodesConnectable(nodesConnectable: boolean) {
this.nodesConnectable = nodesConnectable
- },
-
+ }
}
-});
\ No newline at end of file
+})
\ No newline at end of file
diff --git a/frontend/src/stores/modules/dict.ts b/frontend/src/stores/modules/dict.ts
index e37e779..a622883 100644
--- a/frontend/src/stores/modules/dict.ts
+++ b/frontend/src/stores/modules/dict.ts
@@ -5,11 +5,9 @@ import { DICT_STORE_KEY } from '@/stores/constant'
// 模拟数据
//import dictData from '@/api/system/dictData'
-
-export const useDictStore = defineStore({
- id: DICT_STORE_KEY,
+export const useDictStore = defineStore(DICT_STORE_KEY, {
state: () => ({
- dictData: [] as Dict[],
+ dictData: [] as Dict[]
}),
getters: {},
actions: {
@@ -27,7 +25,7 @@ export const useDictStore = defineStore({
// 初始化获取全部字典数据并缓存
async initDictData(initData: Dict[]) {
this.dictData = initData
- },
+ }
},
- persist: piniaPersistConfig(DICT_STORE_KEY),
+ persist: piniaPersistConfig(DICT_STORE_KEY)
})
diff --git a/frontend/src/stores/modules/global.ts b/frontend/src/stores/modules/global.ts
index 63d7c07..c744548 100644
--- a/frontend/src/stores/modules/global.ts
+++ b/frontend/src/stores/modules/global.ts
@@ -1,55 +1,53 @@
-import { defineStore } from "pinia";
-import { type GlobalState } from "@/stores/interface";
-import { DEFAULT_PRIMARY} from "@/config";
-import piniaPersistConfig from "@/stores/helper/persist";
-import {GLOBAL_STORE_KEY} from "@/stores/constant";
+import { defineStore } from 'pinia'
+import { type GlobalState } from '@/stores/interface'
+import { DEFAULT_PRIMARY } from '@/config'
+import piniaPersistConfig from '@/stores/helper/persist'
+import { GLOBAL_STORE_KEY } from '@/stores/constant'
-export const useGlobalStore = defineStore({
- id: GLOBAL_STORE_KEY,
- // 修改默认值之后,需清除 localStorage 数据
- state: (): GlobalState => ({
- // 布局模式 (纵向:vertical | 经典:classic | 横向:transverse | 分栏:columns)
- layout: "transverse",
- // element 组件大小
- assemblySize: "default",
- // 当前系统语言
- language: null,
- // 当前页面是否全屏
- maximize: false,
- // 主题颜色
- primary: DEFAULT_PRIMARY,
- // 深色模式
- isDark: false,
- // 灰色模式
- isGrey: false,
- // 色弱模式
- isWeak: false,
- // 侧边栏反转
- asideInverted: false,
- // 头部反转
- headerInverted: false,
- // 折叠菜单
- isCollapse: false,
- // 菜单手风琴
- accordion: false,
- // 面包屑导航
- breadcrumb: true,
- // 面包屑导航图标
- breadcrumbIcon: true,
- // 标签页
- tabs: true,
- // 标签页图标
- tabsIcon: true,
- // 页脚
- footer: false
-
- }),
- getters: {},
- actions: {
- // Set GlobalState
- setGlobalState(...args: ObjToKeyValArray) {
- this.$patch({ [args[0]]: args[1] });
- }
- },
- persist: piniaPersistConfig(GLOBAL_STORE_KEY)
-});
+export const useGlobalStore = defineStore(GLOBAL_STORE_KEY, {
+ // 修改默认值之后,需清除 localStorage 数据
+ state: (): GlobalState => ({
+ // 布局模式 (纵向:vertical | 经典:classic | 横向:transverse | 分栏:columns)
+ layout: 'transverse',
+ // element 组件大小
+ assemblySize: 'default',
+ // 当前系统语言
+ language: null,
+ // 当前页面是否全屏
+ maximize: false,
+ // 主题颜色
+ primary: DEFAULT_PRIMARY,
+ // 深色模式
+ isDark: false,
+ // 灰色模式
+ isGrey: false,
+ // 色弱模式
+ isWeak: false,
+ // 侧边栏反转
+ asideInverted: false,
+ // 头部反转
+ headerInverted: false,
+ // 折叠菜单
+ isCollapse: false,
+ // 菜单手风琴
+ accordion: false,
+ // 面包屑导航
+ breadcrumb: true,
+ // 面包屑导航图标
+ breadcrumbIcon: true,
+ // 标签页
+ tabs: true,
+ // 标签页图标
+ tabsIcon: true,
+ // 页脚
+ footer: false
+ }),
+ getters: {},
+ actions: {
+ // Set GlobalState
+ setGlobalState(...args: ObjToKeyValArray) {
+ this.$patch({ [args[0]]: args[1] })
+ }
+ },
+ persist: piniaPersistConfig(GLOBAL_STORE_KEY)
+})
diff --git a/frontend/src/stores/modules/keepAlive.ts b/frontend/src/stores/modules/keepAlive.ts
index 52eb1e4..7b0223c 100644
--- a/frontend/src/stores/modules/keepAlive.ts
+++ b/frontend/src/stores/modules/keepAlive.ts
@@ -1,24 +1,23 @@
-import {defineStore} from "pinia";
-import {KeepAliveState} from "@/stores/interface";
-import {KEEP_ALIVE_STORE_KEY} from '@/stores/constant'
+import { defineStore } from 'pinia'
+import { type KeepAliveState } from '@/stores/interface'
+import { KEEP_ALIVE_STORE_KEY } from '@/stores/constant'
-export const useKeepAliveStore = defineStore({
- id: KEEP_ALIVE_STORE_KEY,
- state: (): KeepAliveState => ({
- keepAliveName: []
- }),
- actions: {
- // Add KeepAliveName
- async addKeepAliveName(name: string) {
- !this.keepAliveName.includes(name) && this.keepAliveName.push(name);
- },
- // Remove KeepAliveName
- async removeKeepAliveName(name: string) {
- this.keepAliveName = this.keepAliveName.filter(item => item !== name);
- },
- // Set KeepAliveName
- async setKeepAliveName(keepAliveName: string[] = []) {
- this.keepAliveName = keepAliveName;
+export const useKeepAliveStore = defineStore(KEEP_ALIVE_STORE_KEY, {
+ state: (): KeepAliveState => ({
+ keepAliveName: []
+ }),
+ actions: {
+ // Add KeepAliveName
+ async addKeepAliveName(name: string) {
+ !this.keepAliveName.includes(name) && this.keepAliveName.push(name)
+ },
+ // Remove KeepAliveName
+ async removeKeepAliveName(name: string) {
+ this.keepAliveName = this.keepAliveName.filter(item => item !== name)
+ },
+ // Set KeepAliveName
+ async setKeepAliveName(keepAliveName: string[] = []) {
+ this.keepAliveName = keepAliveName
+ }
}
- }
-});
+})
diff --git a/frontend/src/stores/modules/mode.ts b/frontend/src/stores/modules/mode.ts
index 99caac0..8c14004 100644
--- a/frontend/src/stores/modules/mode.ts
+++ b/frontend/src/stores/modules/mode.ts
@@ -1,29 +1,17 @@
// src/stores/modules/mode.ts
-import { defineStore } from 'pinia';
-
-// export const useModeStore = defineStore('mode', {
-// state: () => ({
-// currentMode: '' as string,
-// }),
-// actions: {
-// setCurrentMode(modeName: string) {
-// this.currentMode = modeName;
-// },
-// },
-// });
-
+import { defineStore } from 'pinia'
export const useModeStore = defineStore('mode', {
state: () => ({
- currentMode: localStorage.getItem('currentMode') || '' as string,
+ currentMode: localStorage.getItem('currentMode') || ('' as string)
}),
actions: {
- setCurrentMode(modeName: string) {
- this.currentMode = modeName;
- localStorage.setItem('currentMode', modeName); // 保存到 localStorage
- },
- },
- });
+ setCurrentMode(modeName: string) {
+ this.currentMode = modeName
+ localStorage.setItem('currentMode', modeName) // 保存到 localStorage
+ }
+ }
+})
export const useAppSceneStore = defineStore('scene', {
state: () => ({
diff --git a/frontend/src/stores/modules/tabs.ts b/frontend/src/stores/modules/tabs.ts
index 120b622..d9de523 100644
--- a/frontend/src/stores/modules/tabs.ts
+++ b/frontend/src/stores/modules/tabs.ts
@@ -1,81 +1,77 @@
-import router from "@/routers";
-import { defineStore } from "pinia";
-import { getUrlWithParams } from "@/utils";
-import { useKeepAliveStore } from "./keepAlive";
-import { TabsState, TabsMenuProps } from "@/stores/interface";
-import piniaPersistConfig from "@/stores/helper/persist";
-import {TABS_STORE_KEY} from "@/stores/constant";
+import router from '@/routers'
+import { defineStore } from 'pinia'
+import { getUrlWithParams } from '@/utils'
+import { useKeepAliveStore } from './keepAlive'
+import type { TabsMenuProps, TabsState } from '@/stores/interface'
+import { TABS_STORE_KEY } from '@/stores/constant'
-const keepAliveStore = useKeepAliveStore();
-
-export const useTabsStore = defineStore({
- id: TABS_STORE_KEY,
- state: (): TabsState => ({
- tabsMenuList: []
- }),
- actions: {
- // Add Tabs
- async addTabs(tabItem: TabsMenuProps) {
-
- if (this.tabsMenuList.every(item => item.path !== tabItem.path)) {
- if (tabItem?.unshift) {
- this.tabsMenuList.unshift(tabItem);
- }else{
- this.tabsMenuList.push(tabItem);
+const keepAliveStore = useKeepAliveStore()
+export const useTabsStore = defineStore(TABS_STORE_KEY, {
+ state: (): TabsState => ({
+ tabsMenuList: []
+ }),
+ actions: {
+ // Add Tabs
+ async addTabs(tabItem: TabsMenuProps) {
+ if (this.tabsMenuList.every(item => item.path !== tabItem.path)) {
+ if (tabItem?.unshift) {
+ this.tabsMenuList.unshift(tabItem)
+ } else {
+ this.tabsMenuList.push(tabItem)
+ }
+ }
+ if (!keepAliveStore.keepAliveName.includes(tabItem.name) && tabItem.isKeepAlive) {
+ await keepAliveStore.addKeepAliveName(tabItem.name)
+ }
+ },
+ // Remove Tabs
+ async removeTabs(tabPath: string, isCurrent: boolean = true) {
+ if (isCurrent) {
+ this.tabsMenuList.forEach((item, index) => {
+ if (item.path !== tabPath) return
+ const nextTab = this.tabsMenuList[index + 1] || this.tabsMenuList[index - 1]
+ if (!nextTab) return
+ router.push(nextTab.path)
+ })
+ }
+ this.tabsMenuList = this.tabsMenuList.filter(item => item.path !== tabPath)
+ // remove keepalive
+ const tabItem = this.tabsMenuList.find(item => item.path === tabPath)
+ tabItem?.isKeepAlive && (await keepAliveStore.removeKeepAliveName(tabItem.name))
+ },
+ // Close Tabs On Side
+ async closeTabsOnSide(path: string, type: 'left' | 'right') {
+ const currentIndex = this.tabsMenuList.findIndex(item => item.path === path)
+ if (currentIndex !== -1) {
+ const range = type === 'left' ? [0, currentIndex] : [currentIndex + 1, this.tabsMenuList.length]
+ this.tabsMenuList = this.tabsMenuList.filter((item, index) => {
+ return index < range[0] || index >= range[1] || !item.close
+ })
+ }
+ // set keepalive
+ const KeepAliveList = this.tabsMenuList.filter(item => item.isKeepAlive)
+ await keepAliveStore.setKeepAliveName(KeepAliveList.map(item => item.name))
+ },
+ // Close MultipleTab
+ async closeMultipleTab(tabsMenuValue?: string) {
+ this.tabsMenuList = this.tabsMenuList.filter(item => {
+ return item.path === tabsMenuValue || !item.close
+ })
+ // set keepalive
+ const KeepAliveList = this.tabsMenuList.filter(item => item.isKeepAlive)
+ await keepAliveStore.setKeepAliveName(KeepAliveList.map(item => item.name))
+ },
+ // Set Tabs
+ async setTabs(tabsMenuList: TabsMenuProps[]) {
+ this.tabsMenuList = tabsMenuList
+ },
+ // Set Tabs Title
+ async setTabsTitle(title: string) {
+ this.tabsMenuList.forEach(item => {
+ if (item.path == getUrlWithParams()) item.title = title
+ })
}
- }
- if (!keepAliveStore.keepAliveName.includes(tabItem.name) && tabItem.isKeepAlive) {
- keepAliveStore.addKeepAliveName(tabItem.name);
- }
- },
- // Remove Tabs
- async removeTabs(tabPath: string, isCurrent: boolean = true) {
- if (isCurrent) {
- this.tabsMenuList.forEach((item, index) => {
- if (item.path !== tabPath) return;
- const nextTab = this.tabsMenuList[index + 1] || this.tabsMenuList[index - 1];
- if (!nextTab) return;
- router.push(nextTab.path);
- });
- }
- this.tabsMenuList = this.tabsMenuList.filter(item => item.path !== tabPath);
- // remove keepalive
- const tabItem = this.tabsMenuList.find(item => item.path === tabPath);
- tabItem?.isKeepAlive && keepAliveStore.removeKeepAliveName(tabItem.name);
- },
- // Close Tabs On Side
- async closeTabsOnSide(path: string, type: "left" | "right") {
- const currentIndex = this.tabsMenuList.findIndex(item => item.path === path);
- if (currentIndex !== -1) {
- const range = type === "left" ? [0, currentIndex] : [currentIndex + 1, this.tabsMenuList.length];
- this.tabsMenuList = this.tabsMenuList.filter((item, index) => {
- return index < range[0] || index >= range[1] || !item.close;
- });
- }
- // set keepalive
- const KeepAliveList = this.tabsMenuList.filter(item => item.isKeepAlive);
- keepAliveStore.setKeepAliveName(KeepAliveList.map(item => item.name));
- },
- // Close MultipleTab
- async closeMultipleTab(tabsMenuValue?: string) {
- this.tabsMenuList = this.tabsMenuList.filter(item => {
- return item.path === tabsMenuValue || !item.close;
- });
- // set keepalive
- const KeepAliveList = this.tabsMenuList.filter(item => item.isKeepAlive);
- keepAliveStore.setKeepAliveName(KeepAliveList.map(item => item.name));
- },
- // Set Tabs
- async setTabs(tabsMenuList: TabsMenuProps[]) {
- this.tabsMenuList = tabsMenuList;
- },
- // Set Tabs Title
- async setTabsTitle(title: string) {
- this.tabsMenuList.forEach(item => {
- if (item.path == getUrlWithParams()) item.title = title;
- });
}
- }
- // persist: piniaPersistConfig(TABS_STORE_KEY)
-});
+ // persist: piniaPersistConfig(TABS_STORE_KEY)
+})
diff --git a/frontend/src/stores/modules/user.ts b/frontend/src/stores/modules/user.ts
index d32efe0..b1d7b8a 100644
--- a/frontend/src/stores/modules/user.ts
+++ b/frontend/src/stores/modules/user.ts
@@ -1,36 +1,57 @@
-import {defineStore} from "pinia";
-import {UserState} from "@/stores/interface";
-import piniaPersistConfig from "@/stores/helper/persist";
-import {USER_STORE_KEY} from "@/stores/constant";
+import { defineStore } from 'pinia'
+import { type UserState } from '@/stores/interface'
+import piniaPersistConfig from '@/stores/helper/persist'
+import { USER_STORE_KEY } from '@/stores/constant'
+import { logoutApi } from '@/api/user/login'
+import { useAuthStore } from '@/stores/modules/auth'
+import { useAppSceneStore, useModeStore } from '@/stores/modules/mode'
+import { useDictStore } from '@/stores/modules/dict'
-export const useUserStore = defineStore({
- id: USER_STORE_KEY,
- state: (): UserState => ({
- accessToken: "",
- refreshToken: "",
- isRefreshToken:false,
- exp: Number(0),
- userInfo: {id:"", name: "" ,loginName:""},
- }),
- getters: {},
- actions: {
- // Set Token
- setAccessToken(accessToken: string) {
- this.accessToken = accessToken;
+export const useUserStore = defineStore(USER_STORE_KEY, {
+ state: (): UserState => ({
+ accessToken: '',
+ refreshToken: '',
+ isRefreshToken: false,
+ exp: Number(0),
+ userInfo: { id: '', name: '', loginName: '' }
+ }),
+ getters: {},
+ actions: {
+ // Set Token
+ setAccessToken(accessToken: string) {
+ this.accessToken = accessToken
+ },
+ setRefreshToken(refreshToken: string) {
+ this.refreshToken = refreshToken
+ },
+ setIsRefreshToken(isRefreshToken: boolean) {
+ this.isRefreshToken = isRefreshToken
+ },
+ // Set setUserInfo
+ setUserInfo(userInfo: UserState['userInfo']) {
+ this.userInfo = userInfo
+ },
+ setExp(exp: number) {
+ this.exp = exp
+ },
+ async logout() {
+ const authStore = useAuthStore()
+ const modeStore = useModeStore()
+ const appSceneStore = useAppSceneStore()
+ const dictStore = useDictStore()
+ // 1.执行退出登录接口
+ await logoutApi()
+ // 2.清除 Token
+ this.setAccessToken('')
+ this.setRefreshToken('')
+ this.setExp(0)
+ this.setUserInfo({ id: '', name: '', loginName: '' })
+ this.setIsRefreshToken(false)
+ dictStore.setDictData([])
+ modeStore.setCurrentMode('')
+ appSceneStore.setCurrentMode('')
+ await authStore.resetAuthStore()
+ }
},
- setRefreshToken(refreshToken: string) {
- this.refreshToken = refreshToken;
- },
- setIsRefreshToken(isRefreshToken: boolean) {
- this.isRefreshToken = isRefreshToken;
- },
- // Set setUserInfo
- setUserInfo(userInfo: UserState["userInfo"]) {
- this.userInfo = userInfo;
- },
- setExp(exp: number) {
- this.exp = exp;
- }
- },
- persist: piniaPersistConfig(USER_STORE_KEY),
-});
+ persist: piniaPersistConfig(USER_STORE_KEY)
+})
diff --git a/frontend/src/views/home/tabs/model.vue b/frontend/src/views/home/tabs/model.vue
index 6b8dcf5..527f8a3 100644
--- a/frontend/src/views/home/tabs/model.vue
+++ b/frontend/src/views/home/tabs/model.vue
@@ -30,13 +30,15 @@
import { useAuthStore } from '@/stores/modules/auth'
import { useAppSceneStore, useModeStore } from '@/stores/modules/mode' // 引入模式 store
import { getCurrentScene } from '@/api/user/login'
+import { initDynamicRouter } from '@/routers/modules/dynamicRouter'
+import { useTabsStore } from '@/stores/modules/tabs'
const authStore = useAuthStore()
const modeStore = useModeStore() // 使用模式 store
const AppSceneStore = useAppSceneStore()
const activateInfo = authStore.activateInfo
const isActivateOpen = import.meta.env.VITE_ACTIVATE_OPEN
-
+const tabsStore = useTabsStore()
const modeList = [
{
name: '模拟式模块',
@@ -69,10 +71,10 @@ const handelOpen = async (item: any) => {
const { data: scene } = await getCurrentScene() // 获取当前场景
AppSceneStore.setCurrentMode(scene + '') //0:省级平台,1:设备出厂,2:研发自测
await authStore.setShowMenu()
- await authStore.getAuthMenuList()
+ await tabsStore.closeMultipleTab()
+ await initDynamicRouter()
return
}
-const handleSelect = (key: string, keyPath: string[]) => {}
onMounted(() => {})