ai-manus/chat-client/library/components/VabTopBar/index.vue

1041 lines
27 KiB
Vue

<script lang="ts" setup>
import { useSettingsStore } from '@/store/modules/settings'
import { useUserStore } from '@/store/modules/user'
import { useRoutesStore } from '@/store/modules/routes'
import { toLoginRoute } from '@/utils/routes'
import { Save, RotateCcw, Minimize } from 'lucide-vue-next'
const route = useRoute()
const router = useRouter()
const settingsStore = useSettingsStore()
const userStore = useUserStore()
const routesStore = useRoutesStore()
const { logo, title } = storeToRefs(settingsStore)
const { avatar, username } = storeToRefs(userStore)
const { getRoutes: routes } = storeToRefs(routesStore)
const { logout } = userStore
const $sub: any = inject('$sub')
const $unsub: any = inject('$unsub')
const currentDate = ref('')
const currentTime = ref('')
const isCollapsed = ref(false)
const scrollPosition = ref(0)
const showMenuDropdown = ref(false)
const isDragEnabled = ref(true)
const isHomePageActive = ref(false)
// 检查是否在 homePage 路由
const isHomePage = computed(() => {
// 使用响应式状态,实时更新
return isHomePageActive.value
})
// 检查是否为访客模式(通过路由路径判断)
const isGuestMode = computed(() => {
const path = route.path.toLowerCase()
return path.includes('/guest/') || path.includes('guest-homepage') || path.includes('guest/homepage')
})
// 监听 homePage 的挂载和卸载
const handleHomePageMounted = () => {
isHomePageActive.value = true
console.log('HomePage 已挂载,显示功能按钮')
}
const handleHomePageUnmounted = () => {
isHomePageActive.value = false
console.log('HomePage 已卸载,隐藏功能按钮')
}
// 监听路由变化,实时更新按钮显示状态
watch(() => route.path, (newPath) => {
console.log('路由变化:', newPath)
// 如果离开了 homePage 路由,立即隐藏按钮
const path = newPath.toLowerCase()
if (!path.includes('homepage') && !path.includes('home-page')) {
isHomePageActive.value = false
// 同时清理全局标识
if ((window as any).__isHomePage__) {
(window as any).__isHomePage__ = false
}
}
}, { immediate: true })
const updateDateTime = () => {
const now = new Date()
const year = now.getFullYear()
const month = String(now.getMonth() + 1).padStart(2, '0')
const day = String(now.getDate()).padStart(2, '0')
const hours = String(now.getHours()).padStart(2, '0')
const minutes = String(now.getMinutes()).padStart(2, '0')
const seconds = String(now.getSeconds()).padStart(2, '0')
currentDate.value = `${year}-${month}-${day}`
currentTime.value = `${hours}:${minutes}:${seconds}`
}
// 处理滚动事件
const handleScroll = () => {
const currentScroll = window.pageYOffset || document.documentElement.scrollTop
// 向下滚动超过50px时收起
if (currentScroll > 50 && !isCollapsed.value) {
isCollapsed.value = true
}
// 回到顶部时展开
else if (currentScroll <= 10 && isCollapsed.value) {
isCollapsed.value = false
}
scrollPosition.value = currentScroll
}
// 切换展开/收起状态
const toggleCollapse = () => {
isCollapsed.value = !isCollapsed.value
console.log('切换 Header 状态:', isCollapsed.value ? '已收起' : '已展开')
}
// 定义顶部菜单项
const topMenuItems = computed(() => {
const rootRoute = routes.value[0]
if (!rootRoute || !rootRoute.children) return []
const children = rootRoute.children
// 查找特定路由
const intelQa = children.find(r => r.name === 'IntelQa')
const homePage = children.find(r => r.name === 'HomePage')
const datasets = children.find(r => r.name === 'DataSets')
const intelRetrieval = children.find(r => r.name === 'IntelRetrieval')
const commQA = children.find(r => r.name === 'CommQA')
const historicalRecords = children.find(r => r.name === 'historicalRecords')
const userManage = children.find(r => r.name === 'UserManage')
const menuItems = []
// 首页
if (intelQa) {
menuItems.push({
name: '首页',
icon: 'home-4-line',
path: homePage.path
})
}
// 知识库
if (datasets) {
menuItems.push({
name: '知识库',
icon: 'database-2-line',
path: datasets.path
})
}
// 智能检索
if (intelRetrieval) {
menuItems.push({
name: '智能检索',
icon: 'search-line',
path: intelRetrieval.path
})
}
// 通用问答
if (commQA) {
menuItems.push({
name: '通用问答',
icon: 'question-answer-line',
path: commQA.path
})
}
// 历史问答
if (historicalRecords) {
menuItems.push({
name: '历史问答',
icon: 'history-line',
path: historicalRecords.path
})
}
return menuItems
})
// 系统配置子菜单
const systemMenuItems = computed(() => {
const rootRoute = routes.value[0]
if (!rootRoute || !rootRoute.children) return []
const userManage = rootRoute.children.find(r => r.name === 'UserManage')
if (!userManage || !userManage.children) return []
return userManage.children
.filter(child => !child.meta?.hidden)
.map(child => ({
name: child.meta?.title || child.name,
icon: child.meta?.icon || '',
path: child.path
}))
})
let timeInterval: any = null
// 监听消息发送事件,自动收起顶部栏
const handleMessageSent = () => {
if (!isCollapsed.value) {
isCollapsed.value = true
}
}
onMounted(() => {
updateDateTime()
timeInterval = setInterval(updateDateTime, 1000)
// 添加滚动监听
window.addEventListener('scroll', handleScroll, { passive: true })
// 订阅消息发送事件
if ($sub) {
$sub('message-sent', handleMessageSent)
$sub('chat-message-sent', handleMessageSent)
}
// 监听 homePage 的挂载和卸载事件
window.addEventListener('homepage-mounted', handleHomePageMounted)
window.addEventListener('homepage-unmounted', handleHomePageUnmounted)
// 监听来自homePage的拖拽状态更新
window.addEventListener('update-drag-mode', ((e: CustomEvent) => {
isDragEnabled.value = e.detail.enabled
}) as EventListener)
// 初始化时检查是否已经在 homePage
if ((window as any).__isHomePage__) {
isHomePageActive.value = true
}
// 监听路由变化,在特定页面发送消息后自动收起
watch(() => route.path, () => {
// 在智能问答和通用问答页面,监听消息发送
if (route.path.includes('intelQa') || route.path.includes('commQA') || route.path.includes('IntelRetrieval')) {
// 延迟一点收起,等待内容渲染
setTimeout(() => {
const hasContent = document.querySelector('.chat-messages, .message-list, .message-container')
if (hasContent && hasContent.children.length > 0) {
isCollapsed.value = true
}
}, 300)
}
})
})
onUnmounted(() => {
if (timeInterval) {
clearInterval(timeInterval)
}
window.removeEventListener('scroll', handleScroll)
window.removeEventListener('homepage-mounted', handleHomePageMounted)
window.removeEventListener('homepage-unmounted', handleHomePageUnmounted)
window.removeEventListener('update-drag-mode', ((e: CustomEvent) => {
isDragEnabled.value = e.detail.enabled
}) as EventListener)
// 取消订阅
if ($unsub) {
$unsub('message-sent')
$unsub('chat-message-sent')
}
})
const handleLogout = async () => {
await logout()
await router.push(toLoginRoute(route.fullPath))
}
const navigateTo = (path: string) => {
router.push(path)
}
// homePage 专用功能函数
const toggleDragMode = () => {
isDragEnabled.value = !isDragEnabled.value
// 触发自定义事件通知 homePage
window.dispatchEvent(new CustomEvent('toggle-drag-mode', { detail: { enabled: isDragEnabled.value } }))
}
const saveCurrentLayout = () => {
window.dispatchEvent(new CustomEvent('save-layout'))
}
const restoreLayout = () => {
window.dispatchEvent(new CustomEvent('restore-layout'))
}
const toggleHeaderCollapse = () => {
// 调用统一的收起/展开函数
toggleCollapse()
}
</script>
<template>
<div class="vab-top-bar-wrapper">
<div class="vab-top-bar" :class="{ 'is-collapsed': isCollapsed }">
<div class="top-bar-border-top"></div>
<div class="top-bar-content">
<!-- 左侧Logo区域 -->
<div class="top-bar-left">
<div class="logo-section">
<img src="@/assets/BRI.png" alt="BRI Logo" class="logo-image" />
</div>
</div>
<!-- 中部标题区域 -->
<div class="top-bar-center">
<div class="title-section">
<h1 class="main-title">{{ title || '应急指挥智能体' }}</h1>
<div class="datetime-section">
<span class="date-display">{{ currentDate }}</span>
<span class="time-separator">|</span>
<span class="time-display">{{ currentTime }}</span>
</div>
</div>
</div>
<!-- 右侧操作按钮区域 -->
<div class="top-bar-right">
<div class="action-buttons">
<!-- 调试信息 - 开发环境显示 -->
<!-- <span v-if="true" style="color: #00D4FF; font-size: 12px; margin-right: 10px; padding: 4px 8px; background: rgba(0,0,0,0.3); border-radius: 4px;">
路由: {{ route.path }} | 显示按钮: {{ isHomePage }}
</span> -->
<!-- homePage 专用控制按钮 -->
<template v-if="isHomePage">
<!-- 布局控制按钮 -->
<div class="layout-controls">
<button
class="layout-btn hover-glow"
@click="saveCurrentLayout"
title="保存当前布局"
>
<Save :size="18" />
</button>
<button
class="layout-btn hover-glow"
@click="restoreLayout"
title="还原保存的布局"
>
<RotateCcw :size="18" />
</button>
</div>
<!-- 拖拽控制开关 -->
<div class="drag-toggle-container">
<span class="drag-toggle-label">拖拽</span>
<button
class="drag-toggle-btn"
:class="{ 'active': isDragEnabled }"
@click="toggleDragMode"
:title="isDragEnabled ? '关闭拖拽功能' : '开启拖拽功能'"
>
<div class="toggle-slider"></div>
</button>
</div>
<!-- 缩略按钮 -->
<button
class="collapse-btn hover-glow"
@click="toggleHeaderCollapse"
title="缩略到左上角"
>
<Minimize :size="20" />
</button>
</template>
<!-- 导航菜单 - 非访客模式显示 -->
<el-dropdown v-if="!isGuestMode" trigger="hover" @command="navigateTo">
<div class="action-btn menu-btn">
<vab-icon icon="menu-line" />
<span>菜单</span>
</div>
<template #dropdown>
<el-dropdown-menu class="tech-dropdown">
<el-dropdown-item
v-for="item in topMenuItems"
:key="item.path"
:command="item.path"
>
<vab-icon :icon="item.icon" />
<span>{{ item.name }}</span>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<!-- 系统配置下拉菜单 - 非访客模式显示 -->
<el-dropdown v-if="!isGuestMode && systemMenuItems.length > 0" trigger="hover" @command="navigateTo">
<div class="action-btn settings-btn">
<vab-icon icon="settings-3-line" />
<span>系统</span>
</div>
<template #dropdown>
<el-dropdown-menu class="tech-dropdown">
<el-dropdown-item
v-for="subItem in systemMenuItems"
:key="subItem.path"
:command="subItem.path"
>
<vab-icon v-if="subItem.icon" :icon="subItem.icon" />
<span>{{ subItem.name }}</span>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<!-- 用户信息区域 - 非访客模式显示 -->
<div v-if="!isGuestMode" class="user-section">
<el-avatar class="user-avatar" :src="avatar" />
<span class="user-name">{{ username }}</span>
<el-icon class="logout-icon" @click="handleLogout" title="退出登录">
<vab-icon icon="logout-circle-r-line" />
</el-icon>
</div>
<!-- 访客模式提示 -->
<div v-if="isGuestMode" class="guest-indicator">
<div class="guest-badge">
<vab-icon icon="user-line" />
<span>指挥中心</span>
</div>
</div>
</div>
</div>
</div>
<div class="top-bar-border-bottom"></div>
</div>
<!-- 收起后的悬浮按钮 - 移到外部容器 -->
<transition name="float-fade">
<div v-if="isCollapsed" class="floating-toggle-btn" @click="toggleCollapse">
<img src="@/assets/BRI.png" alt="BRI Logo" class="floating-logo" />
</div>
</transition>
</div>
</template>
<style lang="scss" scoped>
// Wrapper - 不占据文档流,避免挤压内容
.vab-top-bar-wrapper {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1003;
pointer-events: none; // 允许点击穿透到下方内容
// 子元素恢复 pointer-events
> * {
pointer-events: auto;
}
}
.vab-top-bar {
width: 100%;
height: 70px;
background: linear-gradient(180deg, rgba(10, 37, 64, 0.95) 0%, rgba(13, 47, 77, 0.95) 100%);
backdrop-filter: blur(10px);
border-bottom: 1px solid rgba(0, 212, 255, 0.3);
box-shadow: 0 4px 20px rgba(0, 212, 255, 0.1);
position: relative;
transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1),
opacity 0.4s ease,
box-shadow 0.3s ease;
animation: slideDownHeader 0.6s cubic-bezier(0.4, 0, 0.2, 1);
&.is-collapsed {
transform: translateY(-100%);
opacity: 0;
pointer-events: none;
box-shadow: none;
}
.top-bar-border-top {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 2px;
background: linear-gradient(90deg,
transparent 0%,
#00d4ff 20%,
#0a8bff 50%,
#00d4ff 80%,
transparent 100%
);
animation: borderFlow 3s linear infinite;
}
.top-bar-border-bottom {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 1px;
background: linear-gradient(90deg,
transparent 0%,
rgba(0, 212, 255, 0.5) 50%,
transparent 100%
);
}
.top-bar-content {
height: 100%;
display: grid;
grid-template-columns: 1fr auto 1fr;
align-items: center;
gap: 20px;
padding: 0 32px;
max-width: 1920px;
margin: 0 auto;
}
.top-bar-left {
display: flex;
align-items: center;
gap: 16px;
.logo-section {
display: flex;
align-items: center;
gap: 15px;
transition: all 0.3s ease;
.logo-image {
height: 40px;
width: auto;
object-fit: contain;
transition: all 0.3s ease;
&:hover {
transform: scale(1.1);
}
}
}
}
.top-bar-center {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.title-section {
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
.main-title {
font-size: 26px;
font-weight: 700;
margin: 0;
background: linear-gradient(135deg, #00D4FF 0%, #1890FF 100%);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
text-shadow: 0 0 20px rgba(0, 212, 255, 0.5);
letter-spacing: 2px;
}
.datetime-section {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
color: #94A3B8;
.date-display {
font-weight: 500;
letter-spacing: 1px;
}
.time-separator {
opacity: 0.5;
}
.time-display {
font-family: 'Courier New', monospace;
font-weight: 600;
color: #00D4FF;
letter-spacing: 1px;
}
}
}
}
.top-bar-right {
flex: 0 0 auto;
display: flex;
justify-content: flex-end;
.action-buttons {
display: flex;
align-items: center;
gap: 8px;
.action-btn {
display: flex;
align-items: center;
gap: 6px;
padding: 8px 16px;
color: #a8d8ea;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
border-radius: 6px;
border: 1px solid transparent;
position: relative;
overflow: hidden;
&::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(0, 212, 255, 0.2), transparent);
transition: left 0.5s ease;
}
&:hover {
color: #00d4ff;
background: rgba(0, 212, 255, 0.1);
border-color: rgba(0, 212, 255, 0.3);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 212, 255, 0.2);
&::before {
left: 100%;
}
:deep(.vab-icon) {
transform: scale(1.1);
filter: drop-shadow(0 0 6px rgba(0, 212, 255, 0.6));
}
}
:deep(.vab-icon) {
font-size: 18px;
transition: all 0.3s ease;
&.dropdown-arrow {
font-size: 14px;
margin-left: 2px;
}
}
&.settings-btn {
padding: 8px 12px;
:deep(.vab-icon) {
font-size: 20px;
}
&:hover {
:deep(.vab-icon) {
animation: rotate 0.6s ease;
}
}
}
}
.user-section {
display: flex;
align-items: center;
gap: 10px;
margin-left: 15px;
padding-left: 15px;
border-left: 1px solid rgba(0, 212, 255, 0.3);
.user-avatar {
width: 36px;
height: 36px;
border: 2px solid rgba(0, 212, 255, 0.5);
box-shadow: 0 0 10px rgba(0, 212, 255, 0.3);
}
.user-name {
color: #ffffff;
font-size: 14px;
font-weight: 500;
max-width: 100px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.logout-icon {
font-size: 20px;
color: #a8d8ea;
cursor: pointer;
transition: all 0.3s ease;
&:hover {
color: #ff6b6b;
transform: rotate(180deg);
}
}
}
}
}
}
@keyframes borderFlow {
0% {
background-position: 0% 50%;
}
100% {
background-position: 200% 50%;
}
}
// 顶部栏初始下滑动画
@keyframes slideDownHeader {
0% {
transform: translateY(-100%);
opacity: 0;
}
60% {
transform: translateY(5px);
opacity: 1;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
// 悬浮切换按钮 - 参考 CLClient HomePage 设计
.floating-toggle-btn {
position: fixed;
top: 12px;
left: 12px;
z-index: 1004;
background: linear-gradient(135deg, rgba(0, 212, 255, 0.15) 0%, rgba(24, 144, 255, 0.15) 100%);
border: 1px solid rgba(0, 212, 255, 0.3);
border-radius: 8px;
padding: 6px;
cursor: pointer;
box-shadow: 0 8px 32px rgba(0, 162, 255, 0.2);
backdrop-filter: blur(15px);
transition: all 0.3s ease;
display: inline-block;
animation: slideInFromLeft 0.5s cubic-bezier(0.4, 0, 0.2, 1);
&:hover {
transform: translateY(-2px);
box-shadow: 0 12px 40px rgba(0, 162, 255, 0.3);
border-color: rgba(0, 212, 255, 0.5);
}
&:active {
transform: scale(0.95);
}
.floating-logo {
height: 32px;
width: auto;
display: block;
filter: drop-shadow(0 0 8px rgba(0, 212, 255, 0.6));
transition: all 0.3s ease;
&:hover {
transform: scale(1.1);
filter: drop-shadow(0 0 12px rgba(0, 212, 255, 0.8));
}
}
}
// 悬浮按钮淡入淡出动画
.float-fade-enter-active {
transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
animation: slideInFromLeft 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}
.float-fade-leave-active {
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
.float-fade-enter-from {
opacity: 0;
transform: translateX(-100px) scale(0.8);
}
.float-fade-leave-to {
opacity: 0;
transform: translateX(-100px) scale(0.8);
}
// 从左侧滑入动画
@keyframes slideInFromLeft {
0% {
opacity: 0;
transform: translateX(-100px) scale(0.8);
}
60% {
opacity: 1;
transform: translateX(5px) scale(1.05);
}
100% {
opacity: 1;
transform: translateX(0) scale(1);
}
}
@keyframes pulse {
0%, 100% {
transform: scale(1);
}
50% {
transform: scale(1.1);
}
}
@keyframes rotate {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
// homePage 专用按钮样式
.layout-controls {
display: flex;
align-items: center;
gap: 8px;
margin-right: 8px;
}
.layout-btn {
background: rgba(0, 162, 255, 0.1);
border: 1px solid rgba(0, 162, 255, 0.3);
border-radius: 6px;
padding: 6px;
color: #00A2FF;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
&:hover {
background: rgba(0, 162, 255, 0.2);
border-color: rgba(0, 162, 255, 0.5);
transform: scale(1.05);
box-shadow: 0 4px 12px rgba(0, 162, 255, 0.2);
}
&:active {
transform: scale(0.95);
}
}
.drag-toggle-container {
display: flex;
align-items: center;
gap: 8px;
margin-right: 8px;
}
.drag-toggle-label {
font-size: 14px;
color: #94A3B8;
font-weight: 500;
}
.drag-toggle-btn {
position: relative;
width: 44px;
height: 24px;
background: rgba(55, 65, 81, 0.6);
border: 1px solid rgba(0, 162, 255, 0.3);
border-radius: 12px;
cursor: pointer;
transition: all 0.3s ease;
outline: none;
&:hover {
border-color: rgba(0, 162, 255, 0.5);
box-shadow: 0 2px 8px rgba(0, 162, 255, 0.2);
}
&.active {
background: rgba(0, 162, 255, 0.2);
border-color: rgba(0, 162, 255, 0.6);
.toggle-slider {
transform: translateX(20px);
background: #00A2FF;
box-shadow: 0 2px 8px rgba(0, 162, 255, 0.4);
}
}
}
.toggle-slider {
position: absolute;
top: 2px;
left: 2px;
width: 18px;
height: 18px;
background: #64748B;
border-radius: 50%;
transition: all 0.3s ease;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
.collapse-btn {
background: rgba(0, 162, 255, 0.1);
border: 1px solid rgba(0, 162, 255, 0.3);
border-radius: 8px;
padding: 8px;
color: #00A2FF;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
margin-right: 8px;
&:hover {
background: rgba(0, 162, 255, 0.2);
border-color: rgba(0, 162, 255, 0.5);
transform: scale(1.05);
box-shadow: 0 4px 12px rgba(0, 162, 255, 0.2);
}
&:active {
transform: scale(0.95);
}
}
// 访客模式指示器样式
.guest-indicator {
display: flex;
align-items: center;
margin-left: 15px;
padding-left: 15px;
border-left: 1px solid rgba(0, 212, 255, 0.3);
}
.guest-badge {
display: flex;
align-items: center;
gap: 6px;
padding: 6px 12px;
background: rgba(250, 173, 20, 0.1);
border: 1px solid rgba(250, 173, 20, 0.3);
border-radius: 16px;
color: #FAAD14;
font-size: 13px;
font-weight: 500;
.vab-icon {
font-size: 16px;
}
}
@media only screen and (max-width: 1200px) {
.top-bar-content {
padding: 0 20px;
}
.top-bar-center {
.title-section {
.main-title {
font-size: 20px;
}
.datetime-section {
font-size: 12px;
}
}
}
.action-btn span {
display: none;
}
.user-section {
.user-name {
display: none;
}
}
.guest-badge {
span {
display: none;
}
}
}
@media only screen and (max-width: 768px) {
.top-bar-content {
grid-template-columns: auto 1fr auto;
gap: 10px;
padding: 0 16px;
}
.top-bar-center {
.title-section {
.main-title {
font-size: 18px;
}
.datetime-section {
display: none;
}
}
}
}
</style>
<style lang="scss">
// 全局样式 - 下拉菜单主题
.tech-dropdown {
background: linear-gradient(135deg, rgba(15, 23, 42, 0.98) 0%, rgba(30, 41, 59, 0.98) 100%) !important;
border: 1px solid rgba(0, 212, 255, 0.5) !important;
border-radius: 12px !important;
backdrop-filter: blur(20px) !important;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.8), 0 0 0 1px rgba(0, 212, 255, 0.2) !important;
padding: 8px !important;
.el-dropdown-menu__item {
color: #ffffff !important;
padding: 10px 16px !important;
border-radius: 6px !important;
transition: all 0.2s ease !important;
&:hover {
background: rgba(0, 212, 255, 0.1) !important;
color: #00D4FF !important;
}
.vab-icon {
margin-right: 8px;
color: #00D4FF;
}
}
}
</style>