Industrial-image-management.../src/layout/components/HeaderRightBar/index.vue

213 lines
5.3 KiB
Vue

<template>
<a-row justify="end" align="center">
<a-space size="medium">
<!-- 搜索 -->
<Search v-if="isDesktop" />
<!-- 项目配置 -->
<a-tooltip content="项目配置" position="bl">
<a-button size="mini" class="gi_hover_btn" @click="SettingDrawerRef?.open">
<template #icon>
<icon-settings :size="18" />
</template>
</a-button>
</a-tooltip>
<!-- 消息通知 -->
<a-popover
position="bottom"
trigger="click"
:content-style="{ marginTop: '-5px', padding: 0, border: 'none' }"
:arrow-style="{ width: 0, height: 0 }"
>
<a-badge :count="unreadMessageCount" dot>
<a-button size="mini" class="gi_hover_btn">
<template #icon>
<icon-notification :size="18" />
</template>
</a-button>
</a-badge>
<template #content>
<Message @readall-success="getMessageCount" />
</template>
</a-popover>
<!-- 全屏切换组件 -->
<a-tooltip v-if="!['xs', 'sm'].includes(breakpoint)" content="全屏切换" position="bottom">
<a-button size="mini" class="gi_hover_btn" @click="toggle">
<template #icon>
<icon-fullscreen v-if="!isFullscreen" :size="18" />
<icon-fullscreen-exit v-else :size="18" />
</template>
</a-button>
</a-tooltip>
<!-- 暗黑模式切换 -->
<a-tooltip content="主题切换" position="bottom">
<GiThemeBtn></GiThemeBtn>
</a-tooltip>
<!-- 管理员账户 -->
<a-dropdown trigger="hover">
<a-row align="center" :wrap="false" class="user">
<!-- 管理员头像 -->
<Avatar :src="userStore.avatar" :name="userStore.nickname" :size="32" />
<span class="username">{{ userStore.nickname }}</span>
<icon-down />
</a-row>
<template #content>
<a-doption @click="router.push('/user/profile')">
<span>个人中心</span>
</a-doption>
<a-doption @click="router.push('/user/message')">
<span>消息中心</span>
</a-doption>
<a-divider :margin="0" />
<a-doption @click="logout">
<span>退出登录</span>
</a-doption>
</template>
</a-dropdown>
</a-space>
</a-row>
<SettingDrawer ref="SettingDrawerRef"></SettingDrawer>
</template>
<script setup lang="ts">
import { Modal } from '@arco-design/web-vue'
import { useFullscreen } from '@vueuse/core'
import { nextTick, onMounted, ref } from 'vue'
import Message from './Message.vue'
import SettingDrawer from './SettingDrawer.vue'
import Search from './Search.vue'
import { useUserStore } from '@/stores'
import { getToken } from '@/utils/auth'
import { useBreakpoint, useDevice } from '@/hooks'
defineOptions({ name: 'HeaderRight' })
const { isDesktop } = useDevice()
const { breakpoint } = useBreakpoint()
let socket: WebSocket | null = null
// 清理函数
onBeforeUnmount(() => {
if (socket) {
socket.close()
socket = null
}
})
const unreadMessageCount = ref(0)
// 初始化 WebSocket - 使用防抖避免重复连接
let initTimer: NodeJS.Timeout | null = null
const initWebSocket = (token: string) => {
if (initTimer) {
clearTimeout(initTimer)
}
initTimer = setTimeout(() => {
// 如果已有连接,先关闭
if (socket) {
socket.close()
socket = null
}
try {
socket = new WebSocket(`${import.meta.env.VITE_API_WS_URL}/websocket?token=${token}`)
socket.onopen = () => {
// console.log('WebSocket connection opened')
}
socket.onmessage = (event) => {
const count = Number.parseInt(event.data)
if (!isNaN(count)) {
unreadMessageCount.value = count
}
}
socket.onerror = () => {
// console.error('WebSocket error:', error)
}
socket.onclose = () => {
// console.log('WebSocket connection closed')
socket = null
}
} catch (error) {
console.error('Failed to create WebSocket connection:', error)
}
initTimer = null
}, 100) // 100ms防抖
}
// 查询未读消息数量
const getMessageCount = async () => {
try {
const token = getToken()
if (token && !socket) {
nextTick(() => {
initWebSocket(token)
})
}
} catch (error) {
console.error('Failed to get message count:', error)
}
}
const { isFullscreen, toggle } = useFullscreen()
const router = useRouter()
const userStore = useUserStore()
const SettingDrawerRef = ref<InstanceType<typeof SettingDrawer>>()
// 退出登录
const logout = () => {
Modal.warning({
title: '提示',
content: '确认退出登录?',
hideCancel: false,
closable: true,
onBeforeOk: async () => {
try {
await userStore.logout()
await router.replace('/login')
return true
} catch (error) {
return false
}
},
})
}
onMounted(() => {
nextTick(() => {
// getMessageCount()
})
})
</script>
<style scoped lang="scss">
.arco-dropdown-open .arco-icon-down {
transform: rotate(180deg);
}
.user {
cursor: pointer;
color: var(--color-text-1);
.username {
margin-left: 10px;
white-space: nowrap;
}
.arco-icon-down {
transition: all 0.3s;
margin-left: 2px;
}
}
</style>