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 showMenuFlag = computed(() => authStore.showMenuFlagGet)
const activeMenu = computed(() => (route.meta.activeMenu ? route.meta.activeMenu : route.path) as string) const activeMenu = computed(() => (route.meta.activeMenu ? route.meta.activeMenu : route.path) as string)
const handleClickMenu = (subItem: Menu.MenuOptions) => { const handleClickMenu = async (subItem: Menu.MenuOptions) => {
if (subItem.meta.isLink) return window.open(subItem.meta.isLink, '_blank') if (subItem.meta?.isLink) {
router.push(subItem.path) window.open(subItem.meta.isLink, '_blank')
return
}
await router.push(subItem.path)
} }
</script> </script>

View File

@@ -28,8 +28,14 @@
import { computed } from 'vue' import { computed } from 'vue'
import { useAuthStore } from '@/stores/modules/auth' import { useAuthStore } from '@/stores/modules/auth'
import { useModeStore } from '@/stores/modules/mode' // 引入模式 store 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 authStore = useAuthStore()
const modeStore = useModeStore() const modeStore = useModeStore()
const tabsStore = useTabsStore()
const title = computed(() => { const title = computed(() => {
return modeStore.currentMode === '' ? '选择模块' : modeStore.currentMode + '模块' return modeStore.currentMode === '' ? '选择模块' : modeStore.currentMode + '模块'
@@ -64,7 +70,9 @@ const handelOpen = async (item: string, key: string) => {
await authStore.setShowMenu() await authStore.setShowMenu()
modeStore.setCurrentMode(item) // 将模式code存入 store modeStore.setCurrentMode(item) // 将模式code存入 store
// 强制刷新页面 // 强制刷新页面
window.location.reload() await tabsStore.closeMultipleTab()
await initDynamicRouter()
await router.push({ path: '/home/index' })
} }
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

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

View File

@@ -52,19 +52,10 @@ import { ElMessage, type FormItemRule } from 'element-plus'
import { updatePassWord } from '@/api/user/user' import { updatePassWord } from '@/api/user/user'
import { type User } from '@/api/user/interface/user' import { type User } from '@/api/user/interface/user'
import { useUserStore } from '@/stores/modules/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 { LOGIN_URL } from '@/config'
import { useAppSceneStore, useModeStore } from '@/stores/modules/mode'
import { UserState } from '@/stores/interface'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
const userStore = useUserStore() const userStore = useUserStore()
const dictStore = useDictStore()
const authStore = useAuthStore()
const modeStore = useModeStore()
const AppSceneStore = useAppSceneStore()
const router = useRouter() const router = useRouter()
// 定义弹出组件元信息 // 定义弹出组件元信息
const dialogFormRef = ref() const dialogFormRef = ref()
@@ -134,19 +125,7 @@ const save = () => {
await updatePassWord(formContent.value) await updatePassWord(formContent.value)
ElMessage.success('修改密码成功,请重新登录!') ElMessage.success('修改密码成功,请重新登录!')
setTimeout(async () => { setTimeout(async () => {
// 1.执行退出登录接口 await userStore.logout()
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 router.push(LOGIN_URL) await router.push(LOGIN_URL)
}, 2000) }, 2000)
} }

View File

@@ -20,27 +20,26 @@
</template> </template>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onBeforeMount } from "vue"; import { useRouter } from 'vue-router'
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);
};
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> </script>
<style lang="scss"> <style lang="scss">
.el-sub-menu .el-sub-menu__title:hover { .el-sub-menu .el-sub-menu__title:hover {
// color: var(--el-menu-hover-text-color) !important; // color: var(--el-menu-hover-text-color) !important;
// background-color: transparent !important; // background-color: transparent !important;
color: #fff !important;//一级导航文字选中颜色 color: #fff !important; //一级导航文字选中颜色
//background-color: #5274a5 !important; //一级导航选中背景色 //background-color: #5274a5 !important; //一级导航选中背景色
background-color: var(--el-color-primary-light-3) !important; background-color: var(--el-color-primary-light-3) !important;
} }
.el-menu--collapse { .el-menu--collapse {
.is-active { .is-active {
@@ -61,7 +60,7 @@ const handleClickMenu = (subItem: Menu.MenuOptions) => {
&.is-active { &.is-active {
// color: var(--el-menu-active-color) !important; // color: var(--el-menu-active-color) !important;
// background-color: var(--el-menu-active-bg-color) !important; // background-color: var(--el-menu-active-bg-color) !important;
color: #fff !important;//一级导航文字选中颜色 color: #fff !important; //一级导航文字选中颜色
//background-color: #5274a5 !important; //一级导航选中背景色 //background-color: #5274a5 !important; //一级导航选中背景色
background-color: var(--el-color-primary-light-3) !important; background-color: var(--el-color-primary-light-3) !important;
@@ -70,7 +69,7 @@ const handleClickMenu = (subItem: Menu.MenuOptions) => {
top: 0; top: 0;
bottom: 0; bottom: 0;
width: 4px; width: 4px;
content: ""; content: '';
background-color: var(--el-color-primary); 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 { staticRouter } from '@/routers/modules/staticRouter'
import NProgress from '@/config/nprogress' import NProgress from '@/config/nprogress'
// 白名单转换为 Set 提高性能
const WHITE_LIST_SET = new Set(ROUTER_WHITE_LIST)
const mode = import.meta.env.VITE_ROUTER_MODE const mode = import.meta.env.VITE_ROUTER_MODE
const routerMode = { const routerMode = {
@@ -30,11 +33,9 @@ const routerMode = {
* @param meta.isKeepAlive ==> 当前路由是否缓存 * @param meta.isKeepAlive ==> 当前路由是否缓存
* */ * */
const router = createRouter({ const router = createRouter({
history: routerMode[mode](), history: routerMode[mode]?.() || createWebHashHistory(), // 默认 fallback 到 hash 模式
routes: [...staticRouter], routes: [...staticRouter],
// 不区分路由大小写,非严格模式下提供了更宽松的路径匹配
strict: false, strict: false,
// 页面刷新时,滚动条位置还原
scrollBehavior: () => ({ left: 0, top: 0 }) scrollBehavior: () => ({ left: 0, top: 0 })
}) })
@@ -44,38 +45,52 @@ const router = createRouter({
router.beforeEach(async (to, from, next) => { router.beforeEach(async (to, from, next) => {
const userStore = useUserStore() const userStore = useUserStore()
const authStore = useAuthStore() const authStore = useAuthStore()
// 1.NProgress 开始 // 1.NProgress 开始
NProgress.start() NProgress.start()
// 2.动态设置标题 // 2.动态设置标题
const title = import.meta.env.VITE_GLOB_APP_TITLE const title = import.meta.env.VITE_GLOB_APP_TITLE
document.title = to.meta.title ? `${to.meta.title} - ${title}` : title document.title = to.meta.title ? `${to.meta.title} - ${title}` : title
// 3.判断是访问登陆页,有 Token 就在当前页,没有 Token 重置路由到登陆页 // 3.判断是访问登陆页,有 Token 就在当前页,没有 Token 重置路由到登陆页
if (to.path.toLocaleLowerCase() === LOGIN_URL) { if (to.path.toLocaleLowerCase() === LOGIN_URL) {
if (userStore.accessToken) return next(from.fullPath) if (userStore.accessToken) {
// 已登录则不再回到登录页,直接跳过
return next('/')
}
resetRouter() resetRouter()
return next() return next()
} }
// 4.判断访问页面是否在路由白名单地址(静态路由)中,如果存在直接放行 // 4.判断访问页面是否在路由白名单地址(静态路由)中,如果存在直接放行
if (ROUTER_WHITE_LIST.includes(to.path)) return next() if (WHITE_LIST_SET.has(to.path)) return next()
// 5.判断是否有 Token没有重定向到 login 页面 // 5.判断是否有 Token没有重定向到 login 页面
if (!userStore.accessToken) return next({ path: LOGIN_URL, replace: true }) if (!userStore.accessToken) return next({ path: LOGIN_URL, replace: true })
// 6.如果没有菜单列表,就重新请求菜单列表并添加动态路由 // 6.如果没有菜单列表,就重新请求菜单列表并添加动态路由
if (!authStore.authMenuListGet.length) { if (!authStore.authMenuListGet.length) {
try {
await initDynamicRouter() await initDynamicRouter()
return next({ ...to, replace: true }) return next({ ...to, replace: true })
} catch (error) {
console.error('动态路由加载失败:', error)
// 清除 token 并跳转登录页
await userStore.logout()
return next({ path: LOGIN_URL, replace: true })
}
} }
// 7.存储 routerName 做按钮权限筛选 // 7.存储 routerName 做按钮权限筛选
await authStore.setRouteName(to.name as string) await authStore.setRouteName(to.name as string)
// 8. 当前页面是否有激活信息,没有就刷新 // 8. 当前页面是否有激活信息,没有就刷新
const activateInfo = authStore.activateInfo const activateInfo = authStore.activateInfo
if (!Object.keys(activateInfo).length) { if (!Object.keys(activateInfo).length) {
await authStore.setActivateInfo() await authStore.setActivateInfo()
} }
// 9.正常访问页面 // 9.正常访问页面
next() next()
}) })
@@ -96,7 +111,7 @@ export const resetRouter = () => {
* */ * */
router.onError(error => { router.onError(error => {
NProgress.done() NProgress.done()
//console.warn('路由错误', error.message) console.warn('路由错误', error.message)
}) })
/** /**

View File

@@ -1,60 +1,117 @@
import router from "@/routers/index"; import router from '@/routers'
import { LOGIN_URL } from "@/config"; import { LOGIN_URL } from '@/config'
import { RouteRecordRaw } from "vue-router"; import { type RouteRecordRaw } from 'vue-router'
import { ElNotification } from "element-plus"; import { ElNotification } from 'element-plus'
import { useUserStore } from "@/stores/modules/user"; import { useUserStore } from '@/stores/modules/user'
import { useAuthStore } from "@/stores/modules/auth"; import { useAuthStore } from '@/stores/modules/auth'
// 引入 views 文件夹下所有 vue 文件 // 引入 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 初始化动态路由 * @description 初始化动态路由
*/ */
export const initDynamicRouter = async () => { export const initDynamicRouter = async () => {
const userStore = useUserStore(); if (isInitializing) return Promise.reject(new Error('Dynamic router initialization in progress'))
const authStore = useAuthStore();
isInitializing = true
const userStore = useUserStore()
const authStore = useAuthStore()
try { try {
// 1.获取菜单列表 && 按钮权限列表 // 1. 获取菜单列表 && 按钮权限列表
await authStore.getAuthMenuList(); await authStore.getAuthMenuList()
await authStore.getAuthButtonList(); await authStore.getAuthButtonList()
// 2.判断当前用户有没有菜单权限 // 2. 判断当前用户有没有菜单权限
if (!authStore.authMenuListGet.length) { if (!authStore.authMenuListGet.length) {
ElNotification({ ElNotification({
title: "无权限访问", title: '无权限访问',
message: "当前账号无任何菜单权限,请联系系统管理员!", message: '当前账号无任何菜单权限,请联系系统管理员!',
type: "warning", type: 'warning',
duration: 3000 duration: 3000
}); })
userStore.setAccessToken(""); userStore.setAccessToken('')
userStore.setRefreshToken(""); userStore.setRefreshToken('')
userStore.setExp(0) userStore.setExp(0)
router.replace(LOGIN_URL); await router.replace(LOGIN_URL)
return Promise.reject("No permission"); return Promise.reject('No permission')
} }
// 3.添加动态路由 // 3. 清理之前的动态路由
authStore.flatMenuListGet.forEach(item => { clearDynamicRoutes()
item.children && delete item.children;
if (item.component && typeof item.component == "string") { // 4. 添加动态路由
item.component = modules["/src/views" + item.component + ".vue"]; 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 { } 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) { } catch (error) {
// 当按钮 || 菜单请求出错时,重定向到登陆页 // 当按钮 || 菜单请求出错时,重定向到登陆页
userStore.setAccessToken(""); userStore.setAccessToken('')
userStore.setRefreshToken(""); userStore.setRefreshToken('')
userStore.setExp(0) userStore.setExp(0)
router.replace(LOGIN_URL); await router.replace(LOGIN_URL)
return Promise.reject(error); 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 */ /* GlobalState */
export interface GlobalState { export interface GlobalState {
layout: LayoutType; layout: LayoutType
assemblySize: AssemblySizeType; assemblySize: AssemblySizeType
language: LanguageType; language: LanguageType
maximize: boolean; maximize: boolean
primary: string; primary: string
isDark: boolean; isDark: boolean
isGrey: boolean; isGrey: boolean
isWeak: boolean; isWeak: boolean
asideInverted: boolean; asideInverted: boolean
headerInverted: boolean; headerInverted: boolean
isCollapse: boolean; isCollapse: boolean
accordion: boolean; accordion: boolean
breadcrumb: boolean; breadcrumb: boolean
breadcrumbIcon: boolean; breadcrumbIcon: boolean
tabs: boolean; tabs: boolean
tabsIcon: boolean; tabsIcon: boolean
footer: boolean; footer: boolean
} }
/* UserState */ /* UserState */
export interface UserState { export interface UserState {
accessToken: string; accessToken: string
refreshToken: string; refreshToken: string
isRefreshToken: boolean; isRefreshToken: boolean
userInfo: { id: string, name: string,loginName:string }; exp: number
userInfo: { id: string; name: string; loginName: string }
} }
/* tabsMenuProps */ /* tabsMenuProps */
export interface TabsMenuProps { export interface TabsMenuProps {
icon: string; icon: string
title: string; title: string
path: string; path: string
name: string; name: string
close: boolean; close: boolean
isKeepAlive: boolean; isKeepAlive: boolean
unshift?: boolean; unshift?: boolean
} }
/* TabsState */ /* TabsState */
export interface TabsState { export interface TabsState {
tabsMenuList: TabsMenuProps[]; tabsMenuList: TabsMenuProps[]
} }
/* AuthState */ /* AuthState */
export interface AuthState { export interface AuthState {
routeName: string; routeName: string
authButtonList: { authButtonList: {
[key: string]: string[]; [key: string]: string[]
}; }
authMenuList: Menu.MenuOptions[]; authMenuList: Menu.MenuOptions[]
showMenuFlag: boolean; showMenuFlag: boolean
activateInfo: Activate.ActivationCodePlaintext
} }
/* KeepAliveState */ /* KeepAliveState */
export interface KeepAliveState { export interface KeepAliveState {
keepAliveName: string[]; keepAliveName: string[]
} }

View File

@@ -1,15 +1,13 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { AuthState } from '@/stores/interface' import { type AuthState } from '@/stores/interface'
import { getAuthButtonListApi, getAuthMenuListApi } from '@/api/user/login' import { getAuthButtonListApi, getAuthMenuListApi } from '@/api/user/login'
import { getAllBreadcrumbList, getFlatMenuList, getShowMenuList } from '@/utils' import { getAllBreadcrumbList, getFlatMenuList, getShowMenuList } from '@/utils'
import { useRouter } from 'vue-router'
import { AUTH_STORE_KEY } from '@/stores/constant' import { AUTH_STORE_KEY } from '@/stores/constant'
import { useModeStore } from '@/stores/modules/mode' import { useModeStore } from '@/stores/modules/mode'
import { getLicense } from '@/api/activate' import { getLicense } from '@/api/activate'
import type { Activate } from '@/api/activate/interface' import type { Activate } from '@/api/activate/interface'
export const useAuthStore = defineStore({ export const useAuthStore = defineStore(AUTH_STORE_KEY, {
id: AUTH_STORE_KEY,
state: (): AuthState => ({ state: (): AuthState => ({
// 按钮权限列表 // 按钮权限列表
authButtonList: {}, authButtonList: {},
@@ -19,8 +17,7 @@ export const useAuthStore = defineStore({
routeName: '', routeName: '',
//登录不显示菜单栏和导航栏,点击进入测试的时候显示 //登录不显示菜单栏和导航栏,点击进入测试的时候显示
showMenuFlag: JSON.parse(localStorage.getItem('showMenuFlag') as string), showMenuFlag: JSON.parse(localStorage.getItem('showMenuFlag') as string),
router: useRouter(), activateInfo: {} as Activate.ActivationCodePlaintext
activateInfo: {}
}), }),
getters: { getters: {
// 按钮权限列表 // 按钮权限列表
@@ -72,10 +69,9 @@ export const useAuthStore = defineStore({
localStorage.setItem('showMenuFlag', 'true') localStorage.setItem('showMenuFlag', 'true')
}, },
//更改模式 //更改模式
async changeModel() { changeModel() {
this.showMenuFlag = false this.showMenuFlag = false
localStorage.removeItem('showMenuFlag') localStorage.removeItem('showMenuFlag')
this.router.push({ path: '/home/index' })
}, },
async setActivateInfo() { async setActivateInfo() {
const license_result = await getLicense() const license_result = await getLicense()

View File

@@ -1,32 +1,37 @@
import {defineStore} from "pinia"; import { defineStore } from 'pinia'
import {CHECK_STORE_KEY} from "@/stores/constant"; import { CHECK_STORE_KEY } from '@/stores/constant'
import type {CheckData} from "@/api/check/interface"; import type { CheckData } from '@/api/check/interface'
import type {Plan} from '@/api/plan/interface' import type { Plan } from '@/api/plan/interface'
import {useAppSceneStore} from "@/stores/modules/mode"; import { useAppSceneStore } from '@/stores/modules/mode'
export const useCheckStore = defineStore(CHECK_STORE_KEY, { export const useCheckStore = defineStore(CHECK_STORE_KEY, {
state: () => ({ state: () => ({
devices: [] as CheckData.Device[], devices: [] as CheckData.Device[],
plan: {} as Plan.ResPlan, 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:自动检测 checkType: 1, // 0:手动检测 1:自动检测
reCheckType: 1, // 0:不合格项复检 1:全部复检 reCheckType: 1, // 0:不合格项复检 1:全部复检
showDetailType: 0, // 0:数据查询 1:误差体系跟换 2正式检测 showDetailType: 0, // 0:数据查询 1:误差体系跟换 2正式检测
temperature: 0, temperature: 0,
humidity: 0, humidity: 0,
chnNumList: [],//连线数据 chnNumList: [] as string[], //连线数据
nodesConnectable: true,//设置是能可以连线 nodesConnectable: true //设置是能可以连线
}), }),
getters: {}, getters: {},
actions: { actions: {
addDevices(device: CheckData.Device[]) { addDevices(device: CheckData.Device[]) {
this.devices.push(...device); this.devices.push(...device)
}, },
setPlan(plan: Plan.ResPlan) { setPlan(plan: Plan.ResPlan) {
this.plan = plan this.plan = plan
}, },
clearDevices() { clearDevices() {
this.devices = []; this.devices = []
}, },
initSelectTestItems() { initSelectTestItems() {
const appSceneStore = useAppSceneStore() const appSceneStore = useAppSceneStore()
@@ -61,7 +66,6 @@ export const useCheckStore = defineStore(CHECK_STORE_KEY, {
}, },
setNodesConnectable(nodesConnectable: boolean) { setNodesConnectable(nodesConnectable: boolean) {
this.nodesConnectable = nodesConnectable this.nodesConnectable = nodesConnectable
},
} }
}); }
})

View File

@@ -5,11 +5,9 @@ import { DICT_STORE_KEY } from '@/stores/constant'
// 模拟数据 // 模拟数据
//import dictData from '@/api/system/dictData' //import dictData from '@/api/system/dictData'
export const useDictStore = defineStore(DICT_STORE_KEY, {
export const useDictStore = defineStore({
id: DICT_STORE_KEY,
state: () => ({ state: () => ({
dictData: [] as Dict[], dictData: [] as Dict[]
}), }),
getters: {}, getters: {},
actions: { actions: {
@@ -27,7 +25,7 @@ export const useDictStore = defineStore({
// 初始化获取全部字典数据并缓存 // 初始化获取全部字典数据并缓存
async initDictData(initData: Dict[]) { async initDictData(initData: Dict[]) {
this.dictData = initData this.dictData = initData
}
}, },
}, persist: piniaPersistConfig(DICT_STORE_KEY)
persist: piniaPersistConfig(DICT_STORE_KEY),
}) })

View File

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

View File

@@ -1,24 +1,23 @@
import {defineStore} from "pinia"; import { defineStore } from 'pinia'
import {KeepAliveState} from "@/stores/interface"; import { type KeepAliveState } from '@/stores/interface'
import {KEEP_ALIVE_STORE_KEY} from '@/stores/constant' import { KEEP_ALIVE_STORE_KEY } from '@/stores/constant'
export const useKeepAliveStore = defineStore({ export const useKeepAliveStore = defineStore(KEEP_ALIVE_STORE_KEY, {
id: KEEP_ALIVE_STORE_KEY,
state: (): KeepAliveState => ({ state: (): KeepAliveState => ({
keepAliveName: [] keepAliveName: []
}), }),
actions: { actions: {
// Add KeepAliveName // Add KeepAliveName
async addKeepAliveName(name: string) { async addKeepAliveName(name: string) {
!this.keepAliveName.includes(name) && this.keepAliveName.push(name); !this.keepAliveName.includes(name) && this.keepAliveName.push(name)
}, },
// Remove KeepAliveName // Remove KeepAliveName
async removeKeepAliveName(name: string) { async removeKeepAliveName(name: string) {
this.keepAliveName = this.keepAliveName.filter(item => item !== name); this.keepAliveName = this.keepAliveName.filter(item => item !== name)
}, },
// Set KeepAliveName // Set KeepAliveName
async setKeepAliveName(keepAliveName: string[] = []) { async setKeepAliveName(keepAliveName: string[] = []) {
this.keepAliveName = keepAliveName; this.keepAliveName = keepAliveName
} }
} }
}); })

View File

@@ -1,29 +1,17 @@
// src/stores/modules/mode.ts // src/stores/modules/mode.ts
import { defineStore } from 'pinia'; import { defineStore } from 'pinia'
// export const useModeStore = defineStore('mode', {
// state: () => ({
// currentMode: '' as string,
// }),
// actions: {
// setCurrentMode(modeName: string) {
// this.currentMode = modeName;
// },
// },
// });
export const useModeStore = defineStore('mode', { export const useModeStore = defineStore('mode', {
state: () => ({ state: () => ({
currentMode: localStorage.getItem('currentMode') || '' as string, currentMode: localStorage.getItem('currentMode') || ('' as string)
}), }),
actions: { actions: {
setCurrentMode(modeName: string) { setCurrentMode(modeName: string) {
this.currentMode = modeName; this.currentMode = modeName
localStorage.setItem('currentMode', modeName); // 保存到 localStorage localStorage.setItem('currentMode', modeName) // 保存到 localStorage
}, }
}, }
}); })
export const useAppSceneStore = defineStore('scene', { export const useAppSceneStore = defineStore('scene', {
state: () => ({ state: () => ({

View File

@@ -1,81 +1,77 @@
import router from "@/routers"; import router from '@/routers'
import { defineStore } from "pinia"; import { defineStore } from 'pinia'
import { getUrlWithParams } from "@/utils"; import { getUrlWithParams } from '@/utils'
import { useKeepAliveStore } from "./keepAlive"; import { useKeepAliveStore } from './keepAlive'
import { TabsState, TabsMenuProps } from "@/stores/interface"; import type { TabsMenuProps, TabsState } from '@/stores/interface'
import piniaPersistConfig from "@/stores/helper/persist"; import { TABS_STORE_KEY } from '@/stores/constant'
import {TABS_STORE_KEY} from "@/stores/constant";
const keepAliveStore = useKeepAliveStore(); const keepAliveStore = useKeepAliveStore()
export const useTabsStore = defineStore({ export const useTabsStore = defineStore(TABS_STORE_KEY, {
id: TABS_STORE_KEY,
state: (): TabsState => ({ state: (): TabsState => ({
tabsMenuList: [] tabsMenuList: []
}), }),
actions: { actions: {
// Add Tabs // Add Tabs
async addTabs(tabItem: TabsMenuProps) { async addTabs(tabItem: TabsMenuProps) {
if (this.tabsMenuList.every(item => item.path !== tabItem.path)) { if (this.tabsMenuList.every(item => item.path !== tabItem.path)) {
if (tabItem?.unshift) { if (tabItem?.unshift) {
this.tabsMenuList.unshift(tabItem); this.tabsMenuList.unshift(tabItem)
}else{ } else {
this.tabsMenuList.push(tabItem); this.tabsMenuList.push(tabItem)
} }
} }
if (!keepAliveStore.keepAliveName.includes(tabItem.name) && tabItem.isKeepAlive) { if (!keepAliveStore.keepAliveName.includes(tabItem.name) && tabItem.isKeepAlive) {
keepAliveStore.addKeepAliveName(tabItem.name); await keepAliveStore.addKeepAliveName(tabItem.name)
} }
}, },
// Remove Tabs // Remove Tabs
async removeTabs(tabPath: string, isCurrent: boolean = true) { async removeTabs(tabPath: string, isCurrent: boolean = true) {
if (isCurrent) { if (isCurrent) {
this.tabsMenuList.forEach((item, index) => { this.tabsMenuList.forEach((item, index) => {
if (item.path !== tabPath) return; if (item.path !== tabPath) return
const nextTab = this.tabsMenuList[index + 1] || this.tabsMenuList[index - 1]; const nextTab = this.tabsMenuList[index + 1] || this.tabsMenuList[index - 1]
if (!nextTab) return; if (!nextTab) return
router.push(nextTab.path); router.push(nextTab.path)
}); })
} }
this.tabsMenuList = this.tabsMenuList.filter(item => item.path !== tabPath); this.tabsMenuList = this.tabsMenuList.filter(item => item.path !== tabPath)
// remove keepalive // remove keepalive
const tabItem = this.tabsMenuList.find(item => item.path === tabPath); const tabItem = this.tabsMenuList.find(item => item.path === tabPath)
tabItem?.isKeepAlive && keepAliveStore.removeKeepAliveName(tabItem.name); tabItem?.isKeepAlive && (await keepAliveStore.removeKeepAliveName(tabItem.name))
}, },
// Close Tabs On Side // Close Tabs On Side
async closeTabsOnSide(path: string, type: "left" | "right") { async closeTabsOnSide(path: string, type: 'left' | 'right') {
const currentIndex = this.tabsMenuList.findIndex(item => item.path === path); const currentIndex = this.tabsMenuList.findIndex(item => item.path === path)
if (currentIndex !== -1) { 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) => { 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 // set keepalive
const KeepAliveList = this.tabsMenuList.filter(item => item.isKeepAlive); const KeepAliveList = this.tabsMenuList.filter(item => item.isKeepAlive)
keepAliveStore.setKeepAliveName(KeepAliveList.map(item => item.name)); await keepAliveStore.setKeepAliveName(KeepAliveList.map(item => item.name))
}, },
// Close MultipleTab // Close MultipleTab
async closeMultipleTab(tabsMenuValue?: string) { async closeMultipleTab(tabsMenuValue?: string) {
this.tabsMenuList = this.tabsMenuList.filter(item => { this.tabsMenuList = this.tabsMenuList.filter(item => {
return item.path === tabsMenuValue || !item.close; return item.path === tabsMenuValue || !item.close
}); })
// set keepalive // set keepalive
const KeepAliveList = this.tabsMenuList.filter(item => item.isKeepAlive); const KeepAliveList = this.tabsMenuList.filter(item => item.isKeepAlive)
keepAliveStore.setKeepAliveName(KeepAliveList.map(item => item.name)); await keepAliveStore.setKeepAliveName(KeepAliveList.map(item => item.name))
}, },
// Set Tabs // Set Tabs
async setTabs(tabsMenuList: TabsMenuProps[]) { async setTabs(tabsMenuList: TabsMenuProps[]) {
this.tabsMenuList = tabsMenuList; this.tabsMenuList = tabsMenuList
}, },
// Set Tabs Title // Set Tabs Title
async setTabsTitle(title: string) { async setTabsTitle(title: string) {
this.tabsMenuList.forEach(item => { this.tabsMenuList.forEach(item => {
if (item.path == getUrlWithParams()) item.title = title; if (item.path == getUrlWithParams()) item.title = title
}); })
} }
} }
// persist: piniaPersistConfig(TABS_STORE_KEY) // persist: piniaPersistConfig(TABS_STORE_KEY)
}); })

View File

@@ -1,36 +1,57 @@
import {defineStore} from "pinia"; import { defineStore } from 'pinia'
import {UserState} from "@/stores/interface"; import { type UserState } from '@/stores/interface'
import piniaPersistConfig from "@/stores/helper/persist"; import piniaPersistConfig from '@/stores/helper/persist'
import {USER_STORE_KEY} from "@/stores/constant"; 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({ export const useUserStore = defineStore(USER_STORE_KEY, {
id: USER_STORE_KEY,
state: (): UserState => ({ state: (): UserState => ({
accessToken: "", accessToken: '',
refreshToken: "", refreshToken: '',
isRefreshToken:false, isRefreshToken: false,
exp: Number(0), exp: Number(0),
userInfo: {id:"", name: "" ,loginName:""}, userInfo: { id: '', name: '', loginName: '' }
}), }),
getters: {}, getters: {},
actions: { actions: {
// Set Token // Set Token
setAccessToken(accessToken: string) { setAccessToken(accessToken: string) {
this.accessToken = accessToken; this.accessToken = accessToken
}, },
setRefreshToken(refreshToken: string) { setRefreshToken(refreshToken: string) {
this.refreshToken = refreshToken; this.refreshToken = refreshToken
}, },
setIsRefreshToken(isRefreshToken: boolean) { setIsRefreshToken(isRefreshToken: boolean) {
this.isRefreshToken = isRefreshToken; this.isRefreshToken = isRefreshToken
}, },
// Set setUserInfo // Set setUserInfo
setUserInfo(userInfo: UserState["userInfo"]) { setUserInfo(userInfo: UserState['userInfo']) {
this.userInfo = userInfo; this.userInfo = userInfo
}, },
setExp(exp: number) { 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 { useAuthStore } from '@/stores/modules/auth'
import { useAppSceneStore, useModeStore } from '@/stores/modules/mode' // 引入模式 store import { useAppSceneStore, useModeStore } from '@/stores/modules/mode' // 引入模式 store
import { getCurrentScene } from '@/api/user/login' import { getCurrentScene } from '@/api/user/login'
import { initDynamicRouter } from '@/routers/modules/dynamicRouter'
import { useTabsStore } from '@/stores/modules/tabs'
const authStore = useAuthStore() const authStore = useAuthStore()
const modeStore = useModeStore() // 使用模式 store const modeStore = useModeStore() // 使用模式 store
const AppSceneStore = useAppSceneStore() const AppSceneStore = useAppSceneStore()
const activateInfo = authStore.activateInfo const activateInfo = authStore.activateInfo
const isActivateOpen = import.meta.env.VITE_ACTIVATE_OPEN const isActivateOpen = import.meta.env.VITE_ACTIVATE_OPEN
const tabsStore = useTabsStore()
const modeList = [ const modeList = [
{ {
name: '模拟式模块', name: '模拟式模块',
@@ -69,10 +71,10 @@ const handelOpen = async (item: any) => {
const { data: scene } = await getCurrentScene() // 获取当前场景 const { data: scene } = await getCurrentScene() // 获取当前场景
AppSceneStore.setCurrentMode(scene + '') //0省级平台1设备出厂2研发自测 AppSceneStore.setCurrentMode(scene + '') //0省级平台1设备出厂2研发自测
await authStore.setShowMenu() await authStore.setShowMenu()
await authStore.getAuthMenuList() await tabsStore.closeMultipleTab()
await initDynamicRouter()
return return
} }
const handleSelect = (key: string, keyPath: string[]) => {}
onMounted(() => {}) onMounted(() => {})
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>