2025-08-27 19:25:29 +08:00
|
|
|
<template>
|
|
|
|
|
<div class="nav-menus" :class="configStore.layout.layoutMode">
|
|
|
|
|
<el-tooltip effect="dark" content="暂降事件" placement="bottom">
|
|
|
|
|
<div @click="temporaryLandingEvent" class="nav-menu-item">
|
|
|
|
|
<Icon
|
|
|
|
|
:color="configStore.getColorVal('headerBarTabColor')"
|
|
|
|
|
class="nav-menu-icon"
|
|
|
|
|
name="el-icon-BellFilled"
|
|
|
|
|
size="18"
|
|
|
|
|
/>
|
2026-06-16 09:56:38 +08:00
|
|
|
<span class="nav-menu-text" v-if="globalPopUpRef?.eventList?.length > 0">
|
2026-06-15 17:57:14 +08:00
|
|
|
{{ globalPopUpRef.eventList.length > 99 ? '99+' : globalPopUpRef.eventList.length }}
|
2025-08-27 19:25:29 +08:00
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
</el-tooltip>
|
2026-06-15 17:57:14 +08:00
|
|
|
|
2025-08-27 19:25:29 +08:00
|
|
|
<el-dropdown style="height: 100%" @command="handleCommand">
|
2026-06-15 17:57:14 +08:00
|
|
|
<div class="admin-info" :class="state.currentNavMenu === 'adminInfo' ? 'hover' : ''">
|
2025-08-27 19:25:29 +08:00
|
|
|
<el-avatar :size="25" fit="fill">
|
|
|
|
|
<img src="@/assets/avatar.png" alt="" />
|
|
|
|
|
</el-avatar>
|
|
|
|
|
<div class="admin-name">{{ adminInfo.nickname }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
<template #dropdown>
|
|
|
|
|
<el-dropdown-menu>
|
|
|
|
|
<el-dropdown-item command="adminInfo">个人资料</el-dropdown-item>
|
|
|
|
|
<el-dropdown-item command="changePwd">修改密码</el-dropdown-item>
|
2026-06-15 17:57:14 +08:00
|
|
|
<el-dropdown-item command="logout">退出登录</el-dropdown-item>
|
2025-08-27 19:25:29 +08:00
|
|
|
</el-dropdown-menu>
|
|
|
|
|
</template>
|
|
|
|
|
</el-dropdown>
|
2026-06-15 17:57:14 +08:00
|
|
|
|
2025-08-27 19:25:29 +08:00
|
|
|
<Config />
|
|
|
|
|
<PopupPwd ref="popupPwd" />
|
|
|
|
|
<AdminInfo ref="popupAdminInfo" />
|
|
|
|
|
<globalPopUp ref="globalPopUpRef" />
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script lang="ts" setup>
|
|
|
|
|
import { reactive, ref } from 'vue'
|
|
|
|
|
import { useConfig } from '@/stores/config'
|
|
|
|
|
import { useAdminInfo } from '@/stores/adminInfo'
|
2026-06-15 17:57:14 +08:00
|
|
|
import { useNavTabs } from '@/stores/navTabs'
|
|
|
|
|
import { logout } from '@/api/user-boot/user'
|
|
|
|
|
import Config from './config.vue'
|
2025-08-27 19:25:29 +08:00
|
|
|
import PopupPwd from './popup/password.vue'
|
|
|
|
|
import AdminInfo from './popup/adminInfo.vue'
|
2026-06-16 09:56:38 +08:00
|
|
|
const { replace } = useRouter()
|
2026-06-15 17:57:14 +08:00
|
|
|
// 环境标识
|
|
|
|
|
const IS_LNQR = import.meta.env.VITE_NAME === 'LNqr'
|
2025-08-27 19:25:29 +08:00
|
|
|
|
|
|
|
|
const adminInfo = useAdminInfo()
|
|
|
|
|
const navTabs = useNavTabs()
|
|
|
|
|
const configStore = useConfig()
|
2026-06-15 17:57:14 +08:00
|
|
|
|
|
|
|
|
// 引用
|
2025-08-27 19:25:29 +08:00
|
|
|
const popupPwd = ref()
|
|
|
|
|
const popupAdminInfo = ref()
|
2026-06-15 17:57:14 +08:00
|
|
|
const globalPopUpRef = ref()
|
|
|
|
|
|
|
|
|
|
// 状态
|
2025-08-27 19:25:29 +08:00
|
|
|
const state = reactive({
|
2026-06-16 09:56:38 +08:00
|
|
|
currentNavMenu: ''
|
2025-08-27 19:25:29 +08:00
|
|
|
})
|
2026-06-15 17:57:14 +08:00
|
|
|
|
|
|
|
|
// 打开暂降事件
|
|
|
|
|
const temporaryLandingEvent = () => {
|
|
|
|
|
if (globalPopUpRef.value) {
|
|
|
|
|
globalPopUpRef.value.open()
|
2025-08-27 19:25:29 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-15 17:57:14 +08:00
|
|
|
// 下拉菜单命令
|
|
|
|
|
const handleCommand = async (key: string) => {
|
2025-08-27 19:25:29 +08:00
|
|
|
switch (key) {
|
|
|
|
|
case 'adminInfo':
|
2026-06-15 17:57:14 +08:00
|
|
|
popupAdminInfo.value?.open()
|
2025-08-27 19:25:29 +08:00
|
|
|
break
|
|
|
|
|
case 'changePwd':
|
2026-06-15 17:57:14 +08:00
|
|
|
popupPwd.value?.open()
|
2025-08-27 19:25:29 +08:00
|
|
|
break
|
2026-06-15 17:57:14 +08:00
|
|
|
case 'logout':
|
|
|
|
|
await handleLogout()
|
2025-08-27 19:25:29 +08:00
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-06-15 17:57:14 +08:00
|
|
|
|
|
|
|
|
// 统一登出逻辑
|
|
|
|
|
const handleLogout = async () => {
|
|
|
|
|
try {
|
|
|
|
|
// 1. 调用后端登出接口
|
|
|
|
|
await logout()
|
|
|
|
|
console.log('✅ 后端登出成功')
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.error('❌ 登出接口异常', err)
|
|
|
|
|
} finally {
|
|
|
|
|
// 2. 清理本地所有数据
|
|
|
|
|
clearLocalData()
|
|
|
|
|
|
|
|
|
|
// 3. LNqr 环境跳 CAS 登出
|
|
|
|
|
if (IS_LNQR) {
|
|
|
|
|
redirectToCasLogout()
|
|
|
|
|
} else {
|
|
|
|
|
// 普通环境直接跳登录页
|
2026-06-16 09:56:38 +08:00
|
|
|
replace('/login')
|
2026-06-15 17:57:14 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 清理本地数据
|
|
|
|
|
const clearLocalData = () => {
|
|
|
|
|
// 清空 localStorage
|
|
|
|
|
window.localStorage.clear()
|
|
|
|
|
|
|
|
|
|
// 清空用户状态
|
|
|
|
|
adminInfo.reset()
|
|
|
|
|
|
|
|
|
|
// 清空标签页
|
|
|
|
|
navTabs.closeTabs()
|
|
|
|
|
|
|
|
|
|
console.log('✅ 本地数据已全部清空')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CAS 登出跳转
|
|
|
|
|
const redirectToCasLogout = () => {
|
|
|
|
|
const casLogoutUrl =
|
|
|
|
|
'http://privilege-epri.dcloud.ln.dc.sgcc.com.cn/cas/logout?service=http://privilege-epri.dcloud.ln.dc.sgcc.com.cn/cas/login?service=http://PQMonitoring.dcloud.ln.dc.sgcc.com.cn'
|
|
|
|
|
window.location.href = casLogoutUrl
|
2026-06-16 09:56:38 +08:00
|
|
|
// 清 Cookie
|
|
|
|
|
document.cookie.split(';').forEach(cookie => {
|
|
|
|
|
const name = cookie.split('=')[0].trim()
|
|
|
|
|
document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`
|
|
|
|
|
})
|
2025-08-27 19:25:29 +08:00
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
|
|
.nav-menus.Default {
|
|
|
|
|
border-radius: var(--el-border-radius-base);
|
|
|
|
|
box-shadow: var(--el-box-shadow-light);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.nav-menus {
|
|
|
|
|
height: 60px;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
margin-left: auto;
|
|
|
|
|
background-color: v-bind('configStore.getColorVal("headerBarBackground")');
|
|
|
|
|
|
|
|
|
|
.nav-menu-item {
|
|
|
|
|
position: relative;
|
|
|
|
|
height: 100%;
|
|
|
|
|
width: 40px;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
|
|
|
|
.nav-menu-icon {
|
|
|
|
|
box-sizing: content-box;
|
|
|
|
|
color: v-bind('configStore.getColorVal("headerBarTabColor")');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
|
.icon {
|
|
|
|
|
animation: twinkle 0.3s ease-in-out;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.admin-info {
|
|
|
|
|
display: flex;
|
|
|
|
|
height: 100%;
|
|
|
|
|
padding: 0 10px;
|
|
|
|
|
align-items: center;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
user-select: none;
|
|
|
|
|
color: v-bind('configStore.getColorVal("headerBarTabColor")');
|
|
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
|
color: v-bind('configStore.getColorVal("headerBarTabActiveColor")');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.admin-name {
|
|
|
|
|
padding-left: 6px;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.nav-menu-item:hover,
|
|
|
|
|
.admin-info:hover,
|
|
|
|
|
.nav-menu-item.hover,
|
|
|
|
|
.admin-info.hover {
|
|
|
|
|
background: v-bind('configStore.getColorVal("headerBarHoverBackground")');
|
|
|
|
|
|
|
|
|
|
.nav-menu-icon {
|
|
|
|
|
color: v-bind('configStore.getColorVal("headerBarTabActiveColor")') !important;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.nav-menu-text {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 13px;
|
2026-06-15 17:57:14 +08:00
|
|
|
left: 20px;
|
2025-08-27 19:25:29 +08:00
|
|
|
font-size: 12px;
|
2026-06-15 17:57:14 +08:00
|
|
|
background: #ff0000;
|
2025-08-27 19:25:29 +08:00
|
|
|
color: #fff;
|
|
|
|
|
border-radius: 5px;
|
|
|
|
|
padding: 0 3px;
|
|
|
|
|
}
|
2026-06-15 17:57:14 +08:00
|
|
|
|
|
|
|
|
@keyframes twinkle {
|
2026-06-16 09:56:38 +08:00
|
|
|
0% {
|
|
|
|
|
transform: scale(0);
|
|
|
|
|
}
|
|
|
|
|
80% {
|
|
|
|
|
transform: scale(1.2);
|
|
|
|
|
}
|
|
|
|
|
100% {
|
|
|
|
|
transform: scale(1);
|
|
|
|
|
}
|
2026-06-15 17:57:14 +08:00
|
|
|
}
|
2025-08-27 19:25:29 +08:00
|
|
|
</style>
|