yeg
2026-09-09 528d52f2c7c6f911c54cae6e3f25500eab4384fe
src/layout/Header.vue
@@ -1,12 +1,23 @@
<script setup lang="ts">
import { computed } from 'vue'
import { computed, onMounted } from 'vue'
import { useRoute } from 'vue-router'
import { getUserInfo } from '@/api/auth'
import { useDashboard, clearDashboard } from '@/stores/dashboard'
import { formatBusinessTime } from '@/utils/businessTime'
import GlobalSearch from '@/components/GlobalSearch.vue'
import NotificationCenter from '@/components/NotificationCenter.vue'
defineProps<{ sidebarCollapsed: boolean }>()
defineEmits<{ toggleMenu: []; toggleSidebar: [] }>()
const { snapshot, loadedAt, error, load } = useDashboard()
onMounted(async () => {
  try {
    const permissions = (await getUserInfo()).permissions || []
    if (permissions.some(value => ['*:*:*', 'dashboard:workbench:list', 'dashboard:workbench:query'].includes(value))) await load()
    else clearDashboard()
  } catch { /* 共享查询错误由顶部状态和首页错误提示展示。 */ }
})
const route = useRoute()
const pageTitle = computed(() => String(route.meta.title || '工作台'))
</script>
@@ -31,9 +42,9 @@
    </div>
    <div class="topbar-actions">
      <div class="sync-state">
      <div v-if="loadedAt" class="sync-state">
        <el-icon><Refresh /></el-icon>
        <span>10 秒前同步</span>
        <span>{{ formatBusinessTime(loadedAt) }} 更新</span>
      </div>
      <GlobalSearch />
      <NotificationCenter />
@@ -41,11 +52,13 @@
  </header>
  <div v-if="!route.path.startsWith('/test-list')" class="system-strip">
    <div><span>系统状态</span><strong class="healthy">NORMAL</strong></div>
    <div><span>在线检测柜</span><strong class="healthy">18 / 36</strong></div>
    <div><span>活动测试</span><strong class="active">12</strong></div>
    <div><span>未处理告警</span><strong class="warning">3</strong></div>
    <div><span>采集延迟</span><strong>26 ms</strong></div>
    <div v-if="error"><span role="alert">概览加载失败</span></div>
    <template v-else-if="snapshot">
      <div><span>在线检测柜</span><strong class="healthy">{{ snapshot.cabinets.filter(item => item.onlineStatus === '在线').length }} / {{ snapshot.cabinets.length }}</strong></div>
      <div><span>检测中测试</span><strong class="active">{{ snapshot.tests.filter(item => item.status === 'RUNNING').length }}</strong></div>
      <div><span>开放异常</span><strong class="warning">{{ snapshot.warnings.length }}</strong></div>
    </template>
    <div v-else><span>暂无已加载概览</span></div>
  </div>
</template>
@@ -68,10 +81,10 @@
  align-items: center;
}
.title-group { gap: 12px; }
.title-group h1 { margin: 0; font-size: 20px; line-height: 1; font-weight: 600; color: var(--ink); }
.title-group { gap: 12px; min-width:0; }
.title-group h1 { overflow:hidden;white-space:nowrap;text-overflow:ellipsis;margin: 0; font-size: 20px; line-height: 1; font-weight: 600; color: var(--ink); }
.title-group > span { color: #7b8794; font-size: 10px; font-weight: 500; }
.topbar-actions { gap: 14px; }
.topbar-actions { gap: 14px; flex-shrink:0; }
.sync-state { gap: 6px; color: #52606d; font-size: 12px; }
.sync-state .el-icon { color: var(--blue); font-size: 14px; }