UPDATE: 1、优化动态加载路由;2、修改模式切换,菜单没刷新等问题

This commit is contained in:
贾同学
2025-10-27 16:27:12 +08:00
parent 9376d702f3
commit 425b54bc44
17 changed files with 507 additions and 466 deletions

View File

@@ -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)
}
</script>

View File

@@ -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' })
}
</script>
<style scoped lang="scss">

View File

@@ -1,12 +1,4 @@
<template>
<!-- <div class="userInfo">-->
<!-- <div class="icon">-->
<!-- <Avatar/>-->
<!-- </div>-->
<!-- <div class="username">-->
<!-- {{ username }}-->
<!-- </div>-->
<!-- </div>-->
<el-dropdown trigger="click">
<div class="userInfo">
<div class="icon">
@@ -80,7 +72,6 @@
import { computed, ref } from 'vue'
import { LOGIN_URL } from '@/config'
import { useRouter } from 'vue-router'
import { logoutApi } from '@/api/user/login'
import { useUserStore } from '@/stores/modules/user'
import { ElMessage, ElMessageBox } from 'element-plus'
import InfoDialog from './InfoDialog.vue'
@@ -90,10 +81,9 @@ import VersionDialog from '@/views/system/versionRegister/index.vue'
import { Avatar, Sunny, Switch, Tools } from '@element-plus/icons-vue'
import { useAuthStore } from '@/stores/modules/auth'
import { useDictStore } from '@/stores/modules/dict'
import { useAppSceneStore, useModeStore } from '@/stores/modules/mode'
import { useTheme } from '@/hooks/useTheme'
import { useAppSceneStore } from '@/stores/modules/mode'
import { useI18n } from 'vue-i18n'
import { updateScene } from '@/api/system/base/index'
import { updateScene } from '@/api/system/base'
const userStore = useUserStore()
const dictStore = useDictStore()
@@ -101,10 +91,6 @@ const username = computed(() => userStore.userInfo.name)
const router = useRouter()
const authStore = useAuthStore()
const modeStore = useModeStore()
const AppSceneStore = useAppSceneStore()
const { changePrimary } = useTheme()
// 初始化 i18n
const { t } = useI18n() // 使用 t 方法替代 $t
@@ -116,21 +102,8 @@ const logout = () => {
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
// 1.执行退出登录接口
await logoutApi()
// 2.清除 Token
userStore.setAccessToken('')
userStore.setRefreshToken('')
userStore.setExp(0)
userStore.setUserInfo({ id: '', name: '' })
userStore.setIsRefreshToken(false)
dictStore.setDictData([])
modeStore.setCurrentMode('')
AppSceneStore.setCurrentMode('')
// 3.重定向到登陆页
ElMessage.success('退出登录成功!')
//重置菜单/导航栏权限
await authStore.resetAuthStore()
await userStore.logout()
await router.push(LOGIN_URL)
})
}
@@ -157,8 +130,9 @@ const changeScene = async (value: string) => {
}
//模式切换
const changeMode = () => {
const changeMode = async () => {
authStore.changeModel()
await router.push('/home/index')
}
</script>

View File

@@ -52,19 +52,10 @@ import { ElMessage, type FormItemRule } from 'element-plus'
import { updatePassWord } from '@/api/user/user'
import { type User } from '@/api/user/interface/user'
import { useUserStore } from '@/stores/modules/user'
import { useAuthStore } from '@/stores/modules/auth'
import { useDictStore } from '@/stores/modules/dict'
import { logoutApi } from '@/api/user/login'
import { LOGIN_URL } from '@/config'
import { useAppSceneStore, useModeStore } from '@/stores/modules/mode'
import { UserState } from '@/stores/interface'
import { useRouter } from 'vue-router'
const userStore = useUserStore()
const dictStore = useDictStore()
const authStore = useAuthStore()
const modeStore = useModeStore()
const AppSceneStore = useAppSceneStore()
const router = useRouter()
// 定义弹出组件元信息
const dialogFormRef = ref()
@@ -134,19 +125,7 @@ const save = () => {
await updatePassWord(formContent.value)
ElMessage.success('修改密码成功,请重新登录!')
setTimeout(async () => {
// 1.执行退出登录接口
await logoutApi()
// 2.清除 Token
userStore.setAccessToken('')
userStore.setRefreshToken('')
userStore.setExp(0)
userStore.setUserInfo({ id: '', name: '' } as UserState['userInfo'])
userStore.setIsRefreshToken(false)
dictStore.setDictData([])
modeStore.setCurrentMode('')
AppSceneStore.setCurrentMode('')
//重置菜单/导航栏权限
await authStore.resetAuthStore()
await userStore.logout()
await router.push(LOGIN_URL)
}, 2000)
}

View File

@@ -20,27 +20,26 @@
</template>
</template>
<script setup lang="ts">
import { onBeforeMount } from "vue";
import { useRouter } from "vue-router";
defineProps<{ menuList: Menu.MenuOptions[] }>();
const router = useRouter();
const handleClickMenu = (subItem: Menu.MenuOptions) => {
//console.log('1456----------------',subItem);
if (subItem.meta.isLink) return window.open(subItem.meta.isLink, "_blank");
router.push(subItem.path);
};
import { useRouter } from 'vue-router'
defineProps<{ menuList: Menu.MenuOptions[] }>()
const router = useRouter()
const handleClickMenu = async (subItem: Menu.MenuOptions) => {
if (subItem.meta?.isLink) {
window.open(subItem.meta.isLink, '_blank')
return
}
await router.push(subItem.path)
}
</script>
<style lang="scss">
.el-sub-menu .el-sub-menu__title:hover {
// color: var(--el-menu-hover-text-color) !important;
// background-color: transparent !important;
color: #fff !important;//一级导航文字选中颜色
color: #fff !important; //一级导航文字选中颜色
//background-color: #5274a5 !important; //一级导航选中背景色
background-color: var(--el-color-primary-light-3) !important;
}
.el-menu--collapse {
.is-active {
@@ -61,7 +60,7 @@ const handleClickMenu = (subItem: Menu.MenuOptions) => {
&.is-active {
// color: var(--el-menu-active-color) !important;
// background-color: var(--el-menu-active-bg-color) !important;
color: #fff !important;//一级导航文字选中颜色
color: #fff !important; //一级导航文字选中颜色
//background-color: #5274a5 !important; //一级导航选中背景色
background-color: var(--el-color-primary-light-3) !important;
@@ -70,7 +69,7 @@ const handleClickMenu = (subItem: Menu.MenuOptions) => {
top: 0;
bottom: 0;
width: 4px;
content: "";
content: '';
background-color: var(--el-color-primary);
}
}

View File

@@ -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) {
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)
})
/**

View File

@@ -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'))
isInitializing = true
const userStore = useUserStore()
const authStore = useAuthStore()
try {
// 1.获取菜单列表 && 按钮权限列表
await authStore.getAuthMenuList();
await authStore.getAuthButtonList();
// 1. 获取菜单列表 && 按钮权限列表
await authStore.getAuthMenuList()
await authStore.getAuthButtonList()
// 2.判断当前用户有没有菜单权限
// 2. 判断当前用户有没有菜单权限
if (!authStore.authMenuListGet.length) {
ElNotification({
title: "无权限访问",
message: "当前账号无任何菜单权限,请联系系统管理员!",
type: "warning",
title: '无权限访问',
message: '当前账号无任何菜单权限,请联系系统管理员!',
type: 'warning',
duration: 3000
});
userStore.setAccessToken("");
userStore.setRefreshToken("");
})
userStore.setAccessToken('')
userStore.setRefreshToken('')
userStore.setExp(0)
router.replace(LOGIN_URL);
return Promise.reject("No permission");
await router.replace(LOGIN_URL)
return Promise.reject('No permission')
}
// 3.添加动态路由
authStore.flatMenuListGet.forEach(item => {
item.children && delete item.children;
// 3. 清理之前的动态路由
clearDynamicRoutes()
if (item.component && typeof item.component == "string") {
item.component = modules["/src/views" + item.component + ".vue"];
}
// 4. 添加动态路由
for (const item of authStore.flatMenuListGet) {
// 删除 children 避免冗余嵌套
if (item.children) delete item.children
if (item.meta.isFull) {
router.addRoute(item as unknown as RouteRecordRaw);
// 处理组件映射
if (item.component && typeof item.component === 'string') {
const moduleLoader = await resolveComponentModule(item.component)
if (moduleLoader) {
item.component = moduleLoader
} else {
router.addRoute("layout", item as unknown as RouteRecordRaw);
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.setAccessToken('')
userStore.setRefreshToken('')
userStore.setExp(0)
router.replace(LOGIN_URL);
return Promise.reject(error);
await router.replace(LOGIN_URL)
return Promise.reject(error)
} finally {
isInitializing = false
}
};
}

View File

@@ -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[]
}

View File

@@ -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()

View File

@@ -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()
@@ -61,7 +66,6 @@ export const useCheckStore = defineStore(CHECK_STORE_KEY, {
},
setNodesConnectable(nodesConnectable: boolean) {
this.nodesConnectable = nodesConnectable
},
}
});
}
})

View File

@@ -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)
})

View File

@@ -1,17 +1,16 @@
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,
export const useGlobalStore = defineStore(GLOBAL_STORE_KEY, {
// 修改默认值之后,需清除 localStorage 数据
state: (): GlobalState => ({
// 布局模式 (纵向vertical | 经典classic | 横向transverse | 分栏columns)
layout: "transverse",
layout: 'transverse',
// element 组件大小
assemblySize: "default",
assemblySize: 'default',
// 当前系统语言
language: null,
// 当前页面是否全屏
@@ -42,14 +41,13 @@ export const useGlobalStore = defineStore({
tabsIcon: true,
// 页脚
footer: false
}),
getters: {},
actions: {
// Set GlobalState
setGlobalState(...args: ObjToKeyValArray<GlobalState>) {
this.$patch({ [args[0]]: args[1] });
this.$patch({ [args[0]]: args[1] })
}
},
persist: piniaPersistConfig(GLOBAL_STORE_KEY)
});
})

View File

@@ -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,
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);
!this.keepAliveName.includes(name) && this.keepAliveName.push(name)
},
// Remove KeepAliveName
async removeKeepAliveName(name: string) {
this.keepAliveName = this.keepAliveName.filter(item => item !== name);
this.keepAliveName = this.keepAliveName.filter(item => item !== name)
},
// Set KeepAliveName
async setKeepAliveName(keepAliveName: string[] = []) {
this.keepAliveName = keepAliveName;
this.keepAliveName = keepAliveName
}
}
});
})

View File

@@ -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
},
},
});
this.currentMode = modeName
localStorage.setItem('currentMode', modeName) // 保存到 localStorage
}
}
})
export const useAppSceneStore = defineStore('scene', {
state: () => ({

View File

@@ -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();
const keepAliveStore = useKeepAliveStore()
export const useTabsStore = defineStore({
id: TABS_STORE_KEY,
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);
this.tabsMenuList.unshift(tabItem)
} else {
this.tabsMenuList.push(tabItem)
}
}
if (!keepAliveStore.keepAliveName.includes(tabItem.name) && tabItem.isKeepAlive) {
keepAliveStore.addKeepAliveName(tabItem.name);
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);
});
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);
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);
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);
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];
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;
});
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));
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;
});
return item.path === tabsMenuValue || !item.close
})
// set keepalive
const KeepAliveList = this.tabsMenuList.filter(item => item.isKeepAlive);
keepAliveStore.setKeepAliveName(KeepAliveList.map(item => item.name));
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;
this.tabsMenuList = tabsMenuList
},
// Set Tabs Title
async setTabsTitle(title: string) {
this.tabsMenuList.forEach(item => {
if (item.path == getUrlWithParams()) item.title = title;
});
if (item.path == getUrlWithParams()) item.title = title
})
}
}
// persist: piniaPersistConfig(TABS_STORE_KEY)
});
})

View File

@@ -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,
export const useUserStore = defineStore(USER_STORE_KEY, {
state: (): UserState => ({
accessToken: "",
refreshToken: "",
isRefreshToken:false,
accessToken: '',
refreshToken: '',
isRefreshToken: false,
exp: Number(0),
userInfo: {id:"", name: "" ,loginName:""},
userInfo: { id: '', name: '', loginName: '' }
}),
getters: {},
actions: {
// Set Token
setAccessToken(accessToken: string) {
this.accessToken = accessToken;
this.accessToken = accessToken
},
setRefreshToken(refreshToken: string) {
this.refreshToken = refreshToken;
this.refreshToken = refreshToken
},
setIsRefreshToken(isRefreshToken: boolean) {
this.isRefreshToken = isRefreshToken;
this.isRefreshToken = isRefreshToken
},
// Set setUserInfo
setUserInfo(userInfo: UserState["userInfo"]) {
this.userInfo = userInfo;
setUserInfo(userInfo: UserState['userInfo']) {
this.userInfo = userInfo
},
setExp(exp: number) {
this.exp = exp;
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()
}
},
persist: piniaPersistConfig(USER_STORE_KEY),
});
persist: piniaPersistConfig(USER_STORE_KEY)
})

View File

@@ -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(() => {})
</script>
<style lang="scss" scoped>