修正了切换角色功能

This commit is contained in:
TermiNexus committed 2026-08-02 19:09:23 +08:00
1 parent f062fb1e29
commit 15bcb072f9
3 files changed
+64 -19

No files matched your search

+17 -3
View File
@@ -36,9 +36,23 @@ export const refreshToken = (): Promise<{ AccessToken: string }> => {
return post<{ AccessToken: string }>('/Auth/RefreshToken') return post<{ AccessToken: string }>('/Auth/RefreshToken')
} }
/** 获取当前用户角色列表 */ /** 角色项(后端 GetUserRoles 返回的元素结构) */
export const getUserRoles = (): Promise<string[]> => { export interface RoleItem {
return get<string[]>('/Auth/GetUserRoles') RoleUUID?: string
RoleName: string
RoleStatus?: string | number
AssignScope?: string
RoleDesc?: string
CompanyName?: string
CompanyShort?: string
CompanyUUID?: string
/** 是否为当前激活角色 */
active?: boolean
}
/** 获取当前用户角色列表(解包后端 data.roles 两层结构) */
export const getUserRoles = (): Promise<RoleItem[]> => {
return get<{ roles: RoleItem[] }>('/Auth/GetUserRoles').then((res) => res.roles || [])
} }
/** 切换当前角色 */ /** 切换当前角色 */
+35 -9
View File
@@ -14,7 +14,7 @@ import { ElMessage } from 'element-plus'
import { useAppStore } from '@/stores/app' import { useAppStore } from '@/stores/app'
import { useUserStore } from '@/stores/user' import { useUserStore } from '@/stores/user'
import { useTabsStore } from '@/stores/tabs' import { useTabsStore } from '@/stores/tabs'
import { getAvatarUrl } from '@/api/auth' import { getAvatarUrl, type RoleItem } from '@/api/auth'
import TabBar from '@/components/TabBar.vue' import TabBar from '@/components/TabBar.vue'
import RouteProgress from '@/components/RouteProgress.vue' import RouteProgress from '@/components/RouteProgress.vue'
import PageMask from '@/components/PageMask.vue' import PageMask from '@/components/PageMask.vue'
@@ -55,14 +55,14 @@ onMounted(() => {
} }
}) })
/** 切换身份/角色 */ /** 切换身份/角色(role 为 RoleItem 对象) */
async function handleSwitchRole(role: string): Promise<void> { async function handleSwitchRole(role: RoleItem): Promise<void> {
if (role === userStore.currentRole || roleSwitching.value) return if (role.RoleName === userStore.currentRole || roleSwitching.value) return
roleSwitching.value = true roleSwitching.value = true
try { try {
await userStore.switchRole(role) await userStore.switchRole(role)
tabsStore.reset() tabsStore.reset()
ElMessage.success(`已切换到:${role}`) ElMessage.success(`已切换到:${role.RoleName}`)
router.push('/dashboard') router.push('/dashboard')
} catch { } catch {
ElMessage.error('身份切换失败') ElMessage.error('身份切换失败')
@@ -223,14 +223,14 @@ const isFaIcon = (icon: string | undefined | null): boolean => {
<div class="user-dropdown-roles-list"> <div class="user-dropdown-roles-list">
<div <div
v-for="role in roles" v-for="role in roles"
:key="role" :key="role.RoleUUID || role.RoleName"
class="user-dropdown-role-item" class="user-dropdown-role-item"
:class="{ active: role === userStore.currentRole }" :class="{ active: role.RoleName === userStore.currentRole }"
@click="handleSwitchRole(role)" @click="handleSwitchRole(role)"
> >
<el-icon v-if="role === userStore.currentRole" class="role-check"><Check /></el-icon> <el-icon v-if="role.RoleName === userStore.currentRole" class="role-check"><Check /></el-icon>
<el-icon v-else class="role-placeholder"><CircleCheck /></el-icon> <el-icon v-else class="role-placeholder"><CircleCheck /></el-icon>
<span>{{ role }}</span> <span>{{ role.RoleName }}</span>
<el-icon v-if="roleSwitching" class="is-loading role-loading"><Loading /></el-icon> <el-icon v-if="roleSwitching" class="is-loading role-loading"><Loading /></el-icon>
</div> </div>
</div> </div>
@@ -439,6 +439,9 @@ const isFaIcon = (icon: string | undefined | null): boolean => {
} }
:deep(.el-menu) { :deep(.el-menu) {
// min-width: 0 覆盖默认 min-width: 240px,允许跟随容器收缩到 64px
min-width: 0;
width: 100%;
border-right: none; border-right: none;
padding: 8px 0; padding: 8px 0;
// 宽度跟随侧边栏平滑收缩 // 宽度跟随侧边栏平滑收缩
@@ -460,6 +463,11 @@ const isFaIcon = (icon: string | undefined | null): boolean => {
transition: opacity 0.25s ease; transition: opacity 0.25s ease;
} }
// 一级菜单(有二级菜单的):覆盖 Element Plus 默认 min-width: 200px,允许跟随容器收缩
:deep(.el-sub-menu) {
min-width: 0;
}
// 二级菜单容器:折叠时高度平滑收起,展开侧边栏后恢复 // 二级菜单容器:折叠时高度平滑收起,展开侧边栏后恢复
// min-width: 0 允许其跟随容器收缩(否则默认 min-width: 200px 会撑住 sub-menu 宽度) // min-width: 0 允许其跟随容器收缩(否则默认 min-width: 200px 会撑住 sub-menu 宽度)
:deep(.el-menu--inline) { :deep(.el-menu--inline) {
@@ -473,6 +481,24 @@ const isFaIcon = (icon: string | undefined | null): boolean => {
// 注意:icon 保持与展开态完全相同的位置(padding/margin 不变),仅宽度收缩 + 文字淡出 // 注意:icon 保持与展开态完全相同的位置(padding/margin 不变),仅宽度收缩 + 文字淡出
&.collapsed { &.collapsed {
width: 64px; width: 64px;
// 一级菜单(sub-menu)宽度锁定为容器宽
:deep(.el-sub-menu) {
width: 100%;
min-width: 0;
}
// sub-menu 标题:padding-left 与 el-menu-item 对齐(icon 原地不动),
// padding-right 收紧(默认 44px 为箭头预留,会撑破 64px)
:deep(.el-sub-menu__title) {
width: 100%;
min-width: 0;
padding-left: 20px;
padding-right: 20px;
}
// 二级菜单容器宽度锁定为容器宽
:deep(.el-menu--inline) {
width: 100%;
min-width: 0;
}
// 隐藏左侧指示条 // 隐藏左侧指示条
:deep(.el-menu-item), :deep(.el-menu-item),
:deep(.el-sub-menu__title) { :deep(.el-sub-menu__title) {
+12 -7
View File
@@ -1,14 +1,14 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { ref, computed } from 'vue' import { ref, computed } from 'vue'
import type { UserInfo, MenuItem } from '@/types/api' import type { UserInfo, MenuItem } from '@/types/api'
import { getCurrentUser, getUserRoles, switchRole as apiSwitchRole, login as apiLogin, logout as apiLogout, type LoginParams } from '@/api/auth' import { getCurrentUser, getUserRoles, switchRole as apiSwitchRole, login as apiLogin, logout as apiLogout, type LoginParams, type RoleItem } from '@/api/auth'
import { getMenuList } from '@/api/menu' import { getMenuList } from '@/api/menu'
/** 用户状态管理:当前登录用户、角色、菜单、权限 */ /** 用户状态管理:当前登录用户、角色、菜单、权限 */
export const useUserStore = defineStore('user', () => { export const useUserStore = defineStore('user', () => {
// === 状态 === // === 状态 ===
const userInfo = ref<UserInfo | null>(null) const userInfo = ref<UserInfo | null>(null)
const roles = ref<string[]>([]) const roles = ref<RoleItem[]>([])
const menus = ref<MenuItem[]>([]) const menus = ref<MenuItem[]>([])
const token = ref<string>('') const token = ref<string>('')
const loading = ref(false) const loading = ref(false)
@@ -23,9 +23,10 @@ export const useUserStore = defineStore('user', () => {
try { try {
const raw = localStorage.getItem(CACHE_KEY) const raw = localStorage.getItem(CACHE_KEY)
if (!raw) return if (!raw) return
const cached = JSON.parse(raw) as { userInfo?: UserInfo | null; menus?: MenuItem[] | null } const cached = JSON.parse(raw) as { userInfo?: UserInfo | null; menus?: MenuItem[] | null; roles?: RoleItem[] | null }
if (cached.userInfo) userInfo.value = cached.userInfo if (cached.userInfo) userInfo.value = cached.userInfo
if (cached.menus) menus.value = cached.menus if (cached.menus) menus.value = cached.menus
if (cached.roles?.length) roles.value = cached.roles
} catch { } catch {
// 缓存损坏:清除避免反复解析失败 // 缓存损坏:清除避免反复解析失败
localStorage.removeItem(CACHE_KEY) localStorage.removeItem(CACHE_KEY)
@@ -37,7 +38,7 @@ export const useUserStore = defineStore('user', () => {
try { try {
localStorage.setItem( localStorage.setItem(
CACHE_KEY, CACHE_KEY,
JSON.stringify({ userInfo: userInfo.value, menus: menus.value }), JSON.stringify({ userInfo: userInfo.value, menus: menus.value, roles: roles.value }),
) )
} catch { } catch {
// 写入失败(隐私模式/配额满):忽略,不影响功能 // 写入失败(隐私模式/配额满):忽略,不影响功能
@@ -76,6 +77,7 @@ export const useUserStore = defineStore('user', () => {
await apiLogin(params) await apiLogin(params)
await fetchUserInfo() await fetchUserInfo()
await fetchMenus() await fetchMenus()
await fetchRoles()
} finally { } finally {
loading.value = false loading.value = false
} }
@@ -125,15 +127,18 @@ export const useUserStore = defineStore('user', () => {
} }
/** 获取角色列表 */ /** 获取角色列表 */
async function fetchRoles(): Promise<string[]> { async function fetchRoles(): Promise<RoleItem[]> {
const list = await getUserRoles() const list = await getUserRoles()
roles.value = list roles.value = list
saveToCache()
return list return list
} }
/** 切换当前角色(身份切换) */ /** 切换当前角色(身份切换) */
async function switchRole(role: string): Promise<void> { async function switchRole(role: RoleItem | string): Promise<void> {
await apiSwitchRole(role) // 传对象时取 RoleName(后端 SwitchRole 期望角色名)
const roleName = typeof role === 'string' ? role : role.RoleName
await apiSwitchRole(roleName)
// 刷新用户信息和菜单(不同角色可能有不同菜单权限) // 刷新用户信息和菜单(不同角色可能有不同菜单权限)
await fetchUserInfo() await fetchUserInfo()
await fetchMenus() await fetchMenus()