修正了切换角色功能
This commit is contained in:
1 parent
f062fb1e29
commit
15bcb072f9
3 files changed
+64
-19
No files matched your search
+17
-3
@@ -36,9 +36,23 @@ export const refreshToken = (): Promise<{ AccessToken: string }> => {
|
||||
return post<{ AccessToken: string }>('/Auth/RefreshToken')
|
||||
}
|
||||
|
||||
/** 获取当前用户角色列表 */
|
||||
export const getUserRoles = (): Promise<string[]> => {
|
||||
return get<string[]>('/Auth/GetUserRoles')
|
||||
/** 角色项(后端 GetUserRoles 返回的元素结构) */
|
||||
export interface RoleItem {
|
||||
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 || [])
|
||||
}
|
||||
|
||||
/** 切换当前角色 */
|
||||
|
||||
@@ -14,7 +14,7 @@ import { ElMessage } from 'element-plus'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { useTabsStore } from '@/stores/tabs'
|
||||
import { getAvatarUrl } from '@/api/auth'
|
||||
import { getAvatarUrl, type RoleItem } from '@/api/auth'
|
||||
import TabBar from '@/components/TabBar.vue'
|
||||
import RouteProgress from '@/components/RouteProgress.vue'
|
||||
import PageMask from '@/components/PageMask.vue'
|
||||
@@ -55,14 +55,14 @@ onMounted(() => {
|
||||
}
|
||||
})
|
||||
|
||||
/** 切换身份/角色 */
|
||||
async function handleSwitchRole(role: string): Promise<void> {
|
||||
if (role === userStore.currentRole || roleSwitching.value) return
|
||||
/** 切换身份/角色(role 为 RoleItem 对象) */
|
||||
async function handleSwitchRole(role: RoleItem): Promise<void> {
|
||||
if (role.RoleName === userStore.currentRole || roleSwitching.value) return
|
||||
roleSwitching.value = true
|
||||
try {
|
||||
await userStore.switchRole(role)
|
||||
tabsStore.reset()
|
||||
ElMessage.success(`已切换到:${role}`)
|
||||
ElMessage.success(`已切换到:${role.RoleName}`)
|
||||
router.push('/dashboard')
|
||||
} catch {
|
||||
ElMessage.error('身份切换失败')
|
||||
@@ -223,14 +223,14 @@ const isFaIcon = (icon: string | undefined | null): boolean => {
|
||||
<div class="user-dropdown-roles-list">
|
||||
<div
|
||||
v-for="role in roles"
|
||||
:key="role"
|
||||
:key="role.RoleUUID || role.RoleName"
|
||||
class="user-dropdown-role-item"
|
||||
:class="{ active: role === userStore.currentRole }"
|
||||
:class="{ active: role.RoleName === userStore.currentRole }"
|
||||
@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>
|
||||
<span>{{ role }}</span>
|
||||
<span>{{ role.RoleName }}</span>
|
||||
<el-icon v-if="roleSwitching" class="is-loading role-loading"><Loading /></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
@@ -439,6 +439,9 @@ const isFaIcon = (icon: string | undefined | null): boolean => {
|
||||
}
|
||||
|
||||
:deep(.el-menu) {
|
||||
// min-width: 0 覆盖默认 min-width: 240px,允许跟随容器收缩到 64px
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
border-right: none;
|
||||
padding: 8px 0;
|
||||
// 宽度跟随侧边栏平滑收缩
|
||||
@@ -460,6 +463,11 @@ const isFaIcon = (icon: string | undefined | null): boolean => {
|
||||
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 宽度)
|
||||
:deep(.el-menu--inline) {
|
||||
@@ -473,6 +481,24 @@ const isFaIcon = (icon: string | undefined | null): boolean => {
|
||||
// 注意:icon 保持与展开态完全相同的位置(padding/margin 不变),仅宽度收缩 + 文字淡出
|
||||
&.collapsed {
|
||||
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-sub-menu__title) {
|
||||
|
||||
+12
-7
@@ -1,14 +1,14 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, computed } from 'vue'
|
||||
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'
|
||||
|
||||
/** 用户状态管理:当前登录用户、角色、菜单、权限 */
|
||||
export const useUserStore = defineStore('user', () => {
|
||||
// === 状态 ===
|
||||
const userInfo = ref<UserInfo | null>(null)
|
||||
const roles = ref<string[]>([])
|
||||
const roles = ref<RoleItem[]>([])
|
||||
const menus = ref<MenuItem[]>([])
|
||||
const token = ref<string>('')
|
||||
const loading = ref(false)
|
||||
@@ -23,9 +23,10 @@ export const useUserStore = defineStore('user', () => {
|
||||
try {
|
||||
const raw = localStorage.getItem(CACHE_KEY)
|
||||
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.menus) menus.value = cached.menus
|
||||
if (cached.roles?.length) roles.value = cached.roles
|
||||
} catch {
|
||||
// 缓存损坏:清除避免反复解析失败
|
||||
localStorage.removeItem(CACHE_KEY)
|
||||
@@ -37,7 +38,7 @@ export const useUserStore = defineStore('user', () => {
|
||||
try {
|
||||
localStorage.setItem(
|
||||
CACHE_KEY,
|
||||
JSON.stringify({ userInfo: userInfo.value, menus: menus.value }),
|
||||
JSON.stringify({ userInfo: userInfo.value, menus: menus.value, roles: roles.value }),
|
||||
)
|
||||
} catch {
|
||||
// 写入失败(隐私模式/配额满):忽略,不影响功能
|
||||
@@ -76,6 +77,7 @@ export const useUserStore = defineStore('user', () => {
|
||||
await apiLogin(params)
|
||||
await fetchUserInfo()
|
||||
await fetchMenus()
|
||||
await fetchRoles()
|
||||
} finally {
|
||||
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()
|
||||
roles.value = list
|
||||
saveToCache()
|
||||
return list
|
||||
}
|
||||
|
||||
/** 切换当前角色(身份切换) */
|
||||
async function switchRole(role: string): Promise<void> {
|
||||
await apiSwitchRole(role)
|
||||
async function switchRole(role: RoleItem | string): Promise<void> {
|
||||
// 传对象时取 RoleName(后端 SwitchRole 期望角色名)
|
||||
const roleName = typeof role === 'string' ? role : role.RoleName
|
||||
await apiSwitchRole(roleName)
|
||||
// 刷新用户信息和菜单(不同角色可能有不同菜单权限)
|
||||
await fetchUserInfo()
|
||||
await fetchMenus()
|
||||
|
||||
Reference in new issue
Block a user