feat(ui): 官网头像下拉显示角色 TAG 与持有的应用

- TAG 显示默认登录角色名,无默认则显示当前激活角色名(跨域 GetUserRoles)
- 下拉新增"我的应用"区块(MyApps,最多 6 个,点击跳 GERP hash 路由,超 6 个显示全部入口)
- 加载失败静默隐藏,不影响主菜单
This commit is contained in:
TermiNexus committed 2026-08-22 01:49:06 +08:00
1 parent d88cbb0911
commit cd687cace7
1 file changed
+118 -1
+118 -1
View File
@@ -6,6 +6,7 @@
* - 菜单项:个人中心/系统设置联动跳转 GERP 对应页面,退出登录清 Cookie
* 样式(gerp-user-dropdown / user-dropdown-*)与 GERP 完全一致。
*/
import { computed, onMounted, ref } from 'vue'
import { useSiteStore } from '@/stores/site'
const props = defineProps<{
@@ -45,6 +46,63 @@ async function handleLogout(): Promise<void> {
siteStore.avatarUrl = ''
window.open('https://gerp.gvsds.com/#/login', '_self')
}
/** 角色项(对齐 GERP RoleItem 关键字段) */
interface DropRoleItem {
RoleName?: string
RoleUUID?: string
active?: boolean
IsDefault?: boolean
}
/** 持有应用项(对齐 GERP AppItem 关键字段) */
interface DropAppItem {
AppName?: string
AppIcon?: string
FrontendRoute?: string
}
const roles = ref<DropRoleItem[]>([])
const apps = ref<DropAppItem[]>([])
/** 角色 TAG:优先默认角色,其次当前激活角色 */
const roleTagText = computed(() => {
const def = roles.value.find((r) => r.IsDefault)
if (def?.RoleName) return def.RoleName
const active = roles.value.find((r) => r.active)
return active?.RoleName || '认证用户'
})
/** 打开应用:跳转 GERP hash 路由(FrontendRoute 或应用中心) */
function openApp(app: DropAppItem): void {
window.open('https://gerp.gvsds.com/#' + (app.FrontendRoute || '/app/mine'), '_blank')
}
/** 加载角色与应用(跨子域共享 Cookie;失败静默,不影响主菜单) */
async function loadExtras(): Promise<void> {
try {
const res = await fetch('https://ugm.gvsds.com/Auth/GetUserRoles', { credentials: 'include' })
if (res.ok) {
const data = await res.json()
roles.value = data?.data?.roles || data?.roles || []
}
} catch {
// 忽略
}
try {
const res = await fetch('https://gerp.gvsds.com/API/App/MyApps', { credentials: 'include' })
if (res.ok) {
const data = await res.json()
apps.value = data?.data?.list || data?.list || []
}
} catch {
// 忽略(CORS/网络异常时隐藏应用区)
}
}
onMounted(() => {
void loadExtras()
})
</script>
<template>
@@ -60,12 +118,29 @@ async function handleLogout(): Promise<void> {
<div class="user-dropdown-info">
<div class="user-dropdown-name">{{ props.displayName }}</div>
<div class="user-dropdown-role">
<el-tag size="small" type="success">认证用户</el-tag>
<el-tag size="small" type="success">{{ roleTagText }}</el-tag>
</div>
<div v-if="props.userEmail" class="user-dropdown-email">{{ props.userEmail }}</div>
<div v-if="props.userName" class="user-dropdown-account">@{{ props.userName }}</div>
</div>
</div>
<!-- 持有的应用(最多 6 个,更多跳应用中心) -->
<div v-if="apps.length" class="user-dropdown-apps">
<div class="user-dropdown-apps-title">我的应用</div>
<button
v-for="app in apps.slice(0, 6)"
:key="app.AppName"
class="user-dropdown-app"
@click="openApp(app)"
>
<span class="user-dropdown-app-dot"></span>
<span class="user-dropdown-app-name">{{ app.AppName }}</span>
</button>
<button v-if="apps.length > 6" class="user-dropdown-app" @click="openApp({ FrontendRoute: '/app/mine' })">
<span class="user-dropdown-app-dot"></span>
<span class="user-dropdown-app-name">全部应用({{ apps.length }})</span>
</button>
</div>
<el-dropdown-item command="profile">
<el-icon><User /></el-icon>个人中心
</el-dropdown-item>
@@ -171,4 +246,46 @@ async function handleLogout(): Promise<void> {
.el-dropdown__popper .el-dropdown-menu.gerp-user-dropdown {
background-color: var(--el-bg-color-overlay);
}
/* 持有的应用区块 */
.user-dropdown-apps {
padding: 8px 10px;
border-bottom: 1px solid var(--el-border-color-light);
}
.user-dropdown-apps-title {
padding: 2px 10px 6px;
font-size: 12px;
color: var(--el-text-color-secondary);
font-weight: 500;
}
.user-dropdown-app {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
padding: 7px 10px;
border: none;
border-radius: 8px;
background: transparent;
font-size: 13px;
color: var(--el-text-color-regular);
cursor: pointer;
transition: background 0.15s;
text-align: left;
}
.user-dropdown-app:hover {
background: var(--el-fill-color-light);
}
.user-dropdown-app-dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--el-color-primary);
flex-shrink: 0;
}
.user-dropdown-app-name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
</style>