Files
xinfra/frontend/src/components/Layout/AppSidebar.vue
T
wonder c1480f3219 feat(frontend): 将"资源管理"更名为"机器管理"
- 路由路径: resource/management → machine/management
- 路由名称: ResourceManagement → MachineManagement
- 页面标题: 资源管理 → 机器管理
- 侧边栏文案: 资源管理 → 机器管理
- 文件物理路径未改动(views/resource/Management.vue)
2026-07-17 15:46:03 +08:00

261 lines
7.1 KiB
Vue

<template>
<aside class="sidebar">
<div class="nav-group">
<div class="nav-label">概览</div>
<router-link to="/dashboard" class="nav-item" active-class="active">
<span class="ic">▣</span>资源大盘
</router-link>
</div>
<div class="nav-group">
<div class="nav-label">业务交付</div>
<router-link to="/service/catalog" class="nav-item" active-class="active">
<span class="ic">▤</span>基础服务
</router-link>
<router-link to="/task/log" class="nav-item" active-class="active">
<span class="ic">▧</span>任务日志<span class="badge">2</span>
</router-link>
</div>
<div class="nav-group">
<div class="nav-label">资源纳管</div>
<router-link to="/machine/management" class="nav-item" active-class="active">
<span class="ic">▥</span>机器管理<span class="badge">CMDB</span>
</router-link>
<router-link to="/infrastructure/cluster" class="nav-item" active-class="active">
<span class="ic">◆</span>集群与容器<span class="badge">3</span>
</router-link>
<router-link to="/business/quota" class="nav-item" active-class="active">
<span class="ic">▦</span>业务配额
</router-link>
</div>
<div class="nav-group">
<div class="nav-label">可观测性</div>
<router-link to="/observable/monitoring" class="nav-item" active-class="active">
<span class="ic">◎</span>监控看板
</router-link>
<router-link to="/observable/alert" class="nav-item" active-class="active">
<span class="ic">⚡</span>告警信息
</router-link>
</div>
<div class="nav-group">
<div class="nav-label">统一入口</div>
<div class="nav-item nav-expand" :class="{ active: isPortalActive }">
<router-link to="/subsystem" class="expand-label">
<span class="ic">↗</span>子系统导航
</router-link>
<span class="expand-toggle" @click="portalExpanded = !portalExpanded">
{{ portalExpanded ? '▾' : '▸' }}
</span>
</div>
<div v-show="portalExpanded" class="nav-children">
<router-link
v-for="sys in subsystems"
:key="sys.name"
:to="`/subsystem/detail/${sys.name.toLowerCase()}`"
class="nav-item nav-child"
active-class="active"
>
<span class="child-dot" :style="{ background: dotColor(sys.icon) }"></span>{{ sys.label }} {{ sys.name }}
</router-link>
<router-link to="/subsystem" class="nav-item nav-child" active-class="active">
<span class="ic" style="font-size:12px;">▦</span>全部总览
</router-link>
</div>
<router-link to="/subsystem/authorization" class="nav-item" active-class="active">
<span class="ic">▦</span>子系统赋权
</router-link>
<router-link v-if="isPlatformAdmin" to="/business-line/manage" class="nav-item" active-class="active">
<span class="ic">▤</span>业务线管理
</router-link>
<router-link v-if="isBusinessLineAdmin" to="/business-line/assignment" class="nav-item" active-class="active">
<span class="ic">▥</span>业务线分配
</router-link>
</div>
<div class="nav-group">
<div class="nav-label">审计</div>
<router-link to="/audit/login" class="nav-item" active-class="active">
<span class="ic">📋</span>登录审计
</router-link>
<router-link to="/audit/operation" class="nav-item" active-class="active">
<span class="ic">📝</span>运维操作审计
</router-link>
</div>
<div class="sidebar-foot">
</div>
</aside>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { useRoute } from 'vue-router'
import { useAuthStore } from '@/stores/auth'
import { useBusinessLineStore } from '@/stores/businessLine'
const route = useRoute()
const authStore = useAuthStore()
const businessLineStore = useBusinessLineStore()
const portalExpanded = ref(true)
const isPlatformAdmin = computed(() => authStore.user?.is_admin === true)
const isBusinessLineAdmin = computed(() => businessLineStore.isCurrentAdmin)
const subsystems = [
{ name: 'Wayne', label: '多集群发布平台', icon: 'W' },
{ name: 'CloudDM', label: 'SQL 审核平台', icon: 'DM' },
{ name: 'CacheCloud', label: 'Redis 管理平台', icon: 'CC' },
{ name: 'Apollo', label: '配置中心', icon: 'AP' },
{ name: 'qpass', label: '七牛统一运维平台', icon: 'QP' },
{ name: 'Grafana', label: '指标可视化平台', icon: 'GF' },
{ name: 'Superset', label: '日志数据可视化', icon: 'SS' },
]
const isPortalActive = computed(() => {
return route.path.startsWith('/subsystem/detail/') || route.path === '/subsystem'
})
function dotColor(icon: string): string {
const colors: Record<string, string> = {
W: 'var(--tag-blue-text)',
DM: 'var(--tag-green-text)',
CC: 'var(--err)',
AP: 'var(--tag-blue-text)',
QP: 'var(--tag-amber-text)',
GF: 'var(--warn)',
SS: 'var(--tag-cyan-text)',
}
return colors[icon] || 'var(--text-dim)'
}
</script>
<style scoped>
.sidebar {
background: var(--bg-panel);
border-right: 1px solid var(--line);
padding: 14px 10px;
display: flex;
flex-direction: column;
overflow-y: auto;
}
.nav-group {
margin-bottom: 18px;
}
.nav-label {
font-size: 13px;
color: var(--text-dim);
text-transform: uppercase;
letter-spacing: 1px;
padding: 0 10px;
margin-bottom: 6px;
font-weight: 600;
}
.nav-item {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 10px;
border-radius: 6px;
color: var(--text-mid);
font-size: 16px;
font-weight: 500;
cursor: pointer;
margin-bottom: 2px;
border: 1px solid transparent;
text-decoration: none;
}
.nav-item .ic {
width: 16px;
text-align: center;
font-size: 16px;
opacity: 0.85;
}
.nav-item:hover {
background: var(--bg-panel-2);
color: var(--text-hi);
}
.nav-item.active {
background: var(--accent-dim);
color: var(--accent);
border-color: var(--accent);
}
.nav-item.active .expand-label {
color: var(--accent);
}
.nav-item .badge {
margin-left: auto;
font-family: var(--mono);
font-size: 12px;
background: var(--bg-panel-2);
color: var(--text-dim);
padding: 1px 6px;
border-radius: 10px;
border: 1px solid var(--line);
}
.nav-item.active .badge {
background: var(--accent);
color: #FFFFFF;
border-color: var(--accent);
}
.sidebar-foot {
margin-top: auto;
padding: 10px;
border-top: 1px solid var(--line-soft);
font-size: 14px;
color: var(--text-dim);
line-height: 1.6;
}
.nav-expand {
justify-content: flex-start;
padding: 0;
user-select: none;
}
.expand-label {
display: flex;
align-items: center;
gap: 10px;
flex: 1;
padding: 10px;
text-decoration: none;
color: inherit;
}
.expand-toggle {
padding: 10px;
font-size: 12px;
opacity: 0.6;
cursor: pointer;
border-radius: 0 6px 6px 0;
}
.expand-toggle:hover {
opacity: 1;
background: var(--bg-panel-2);
}
.nav-children {
padding-left: 8px;
}
.nav-child {
padding: 7px 10px 7px 26px;
font-size: 14px;
gap: 8px;
}
.child-dot {
width: 7px;
height: 7px;
border-radius: 50%;
flex-shrink: 0;
}
</style>