feat(container): show business line workloads from Wayne
This commit is contained in:
@@ -36,6 +36,40 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-head">
|
||||
<h3>容器化服务 · 资源概览</h3>
|
||||
<span class="meta">{{ containerLoading ? '同步中...' : containerMetaText }}</span>
|
||||
</div>
|
||||
<div class="container-overview">
|
||||
<div class="container-metric">
|
||||
<span>命名空间</span>
|
||||
<strong>{{ containerSummary.namespaces }}</strong>
|
||||
<small>{{ currentName }} 业务线绑定</small>
|
||||
</div>
|
||||
<div class="container-metric">
|
||||
<span>工作负载</span>
|
||||
<strong>{{ containerSummary.workloads }}</strong>
|
||||
<small>Deployment</small>
|
||||
</div>
|
||||
<div class="container-metric">
|
||||
<span>Pod 实例</span>
|
||||
<strong>{{ containerSummary.pods }}</strong>
|
||||
<small>{{ containerSummary.readyPods }} ready</small>
|
||||
</div>
|
||||
<div class="container-metric">
|
||||
<span>CPU 已用</span>
|
||||
<strong>{{ formatMetric(containerSummary.cpuUsed) }}C</strong>
|
||||
<small>{{ containerSummary.cpuPercent }}% of allocatable</small>
|
||||
</div>
|
||||
<div class="container-metric">
|
||||
<span>Memory 已用</span>
|
||||
<strong>{{ formatMetric(containerSummary.memoryUsed) }}G</strong>
|
||||
<small>{{ containerSummary.memoryPercent }}% of allocatable</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cols-2">
|
||||
<div class="panel">
|
||||
<div class="panel-head">
|
||||
@@ -121,13 +155,57 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { containerServiceApi, emptyContainerServiceSummary, type ContainerServiceSummary } from '@/api/containerService'
|
||||
import { useBusinessLineStore } from '@/stores/businessLine'
|
||||
import { useBusinessLineMockProfile } from '@/utils/businessLineMock'
|
||||
|
||||
const { currentName, profile } = useBusinessLineMockProfile()
|
||||
const businessLineStore = useBusinessLineStore()
|
||||
|
||||
const containerSummary = ref<ContainerServiceSummary>({ ...emptyContainerServiceSummary })
|
||||
const containerLoading = ref(false)
|
||||
const containerError = ref('')
|
||||
|
||||
const containerMetaText = computed(() => {
|
||||
if (containerError.value) {
|
||||
return containerError.value
|
||||
}
|
||||
const errors = containerSummary.value.errors?.length || 0
|
||||
return errors > 0 ? `数据源:Kubernetes 集群运行态 · ${errors} 项同步失败` : '数据源:Kubernetes 集群运行态'
|
||||
})
|
||||
|
||||
const loadContainerSummary = async () => {
|
||||
const businessLineId = businessLineStore.current?.id
|
||||
if (!businessLineId) {
|
||||
containerSummary.value = { ...emptyContainerServiceSummary }
|
||||
return
|
||||
}
|
||||
containerLoading.value = true
|
||||
containerError.value = ''
|
||||
try {
|
||||
containerSummary.value = await containerServiceApi.getSummary(businessLineId)
|
||||
} catch (error) {
|
||||
containerSummary.value = { ...emptyContainerServiceSummary }
|
||||
containerError.value = error instanceof Error ? error.message : '容器化服务数据同步失败'
|
||||
} finally {
|
||||
containerLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const refresh = () => {
|
||||
// 刷新数据
|
||||
loadContainerSummary()
|
||||
}
|
||||
|
||||
const formatMetric = (value: number) => Number(value || 0).toFixed(1).replace(/\.0$/, '')
|
||||
|
||||
watch(
|
||||
() => businessLineStore.current?.id,
|
||||
() => {
|
||||
loadContainerSummary()
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -202,4 +280,40 @@ const refresh = () => {
|
||||
font-size: 11px;
|
||||
color: var(--text-dim);
|
||||
}
|
||||
|
||||
.container-overview {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.container-metric {
|
||||
min-width: 0;
|
||||
padding: 13px 14px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius-lg);
|
||||
background: var(--bg-panel-2);
|
||||
}
|
||||
|
||||
.container-metric span,
|
||||
.container-metric small {
|
||||
display: block;
|
||||
color: var(--text-dim);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.container-metric strong {
|
||||
display: block;
|
||||
margin: 6px 0 4px;
|
||||
color: var(--text-hi);
|
||||
font-family: var(--mono);
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
@media (max-width: 960px) {
|
||||
.container-overview {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -107,14 +107,58 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-head">
|
||||
<h3>容器化服务台账 · 按集群 / Namespace 同步</h3>
|
||||
<span class="meta">{{ containerLoading ? '同步中...' : containerMetaText }}</span>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>服务名</th>
|
||||
<th>集群</th>
|
||||
<th>Namespace</th>
|
||||
<th>工作负载</th>
|
||||
<th>Pod Ready</th>
|
||||
<th>镜像</th>
|
||||
<th>业务标签</th>
|
||||
<th>状态</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-if="!containerLoading && containerServices.length === 0">
|
||||
<td colspan="8" class="text-dim">当前业务线暂无容器化服务</td>
|
||||
</tr>
|
||||
<tr v-for="service in containerServices" :key="`${service.cluster}-${service.namespace}-${service.name}`" class="tr-hover">
|
||||
<td class="strong mono">{{ service.name }}</td>
|
||||
<td><span class="tag">{{ service.cluster }}</span></td>
|
||||
<td class="mono">{{ service.namespace }}</td>
|
||||
<td>{{ service.workload }}</td>
|
||||
<td class="mono">{{ service.ready }}</td>
|
||||
<td class="mono text-xs">{{ service.image || '-' }}</td>
|
||||
<td class="mono">{{ service.biz }}</td>
|
||||
<td :class="['status-text', service.statusClass]">● {{ service.status }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="pagination">
|
||||
<span>共 {{ containerServices.length }} 条容器化服务 · 当前业务线:{{ currentName }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { containerServiceApi, emptyContainerServiceSummary, type ContainerServiceSummary, type ContainerWorkload } from '@/api/containerService'
|
||||
import { useBusinessLineStore } from '@/stores/businessLine'
|
||||
import { useBusinessLineMockProfile } from '@/utils/businessLineMock'
|
||||
|
||||
const { currentName } = useBusinessLineMockProfile()
|
||||
const businessLineStore = useBusinessLineStore()
|
||||
|
||||
const services = ref([
|
||||
{ name: 'kodo-gateway-svc', dc: 'YZH', dcClass: 'zone-a', biz: 'kodo', instances: 12, healthy: '12 / 12', ip: '10.21.4.51', status: '健康', statusClass: 'ok' },
|
||||
@@ -125,8 +169,50 @@ const services = ref([
|
||||
{ name: 'las-order-svc', dc: '达拉斯 IDC', dcClass: '', biz: 'las', instances: 5, healthy: '5 / 5', ip: '10.66.2.20', status: '健康', statusClass: 'ok' },
|
||||
])
|
||||
|
||||
const containerServices = ref<ContainerWorkload[]>([])
|
||||
const containerSummary = ref<ContainerServiceSummary>({ ...emptyContainerServiceSummary })
|
||||
const containerLoading = ref(false)
|
||||
const containerError = ref('')
|
||||
|
||||
const filteredServices = computed(() => services.value.filter((service) => service.biz === currentName.value))
|
||||
const serviceInstances = computed(() => filteredServices.value.reduce((sum, service) => sum + service.instances, 0))
|
||||
const containerMetaText = computed(() => {
|
||||
if (containerError.value) {
|
||||
return containerError.value
|
||||
}
|
||||
const errors = containerSummary.value.errors?.length || 0
|
||||
return errors > 0 ? `数据源:Kubernetes Workload API · ${errors} 项同步失败` : '数据源:Kubernetes Workload API'
|
||||
})
|
||||
|
||||
const loadContainerServices = async () => {
|
||||
const businessLineId = businessLineStore.current?.id
|
||||
if (!businessLineId) {
|
||||
containerServices.value = []
|
||||
containerSummary.value = { ...emptyContainerServiceSummary }
|
||||
return
|
||||
}
|
||||
containerLoading.value = true
|
||||
containerError.value = ''
|
||||
try {
|
||||
const data = await containerServiceApi.listWorkloads(businessLineId)
|
||||
containerServices.value = data.items
|
||||
containerSummary.value = data.summary
|
||||
} catch (error) {
|
||||
containerServices.value = []
|
||||
containerSummary.value = { ...emptyContainerServiceSummary }
|
||||
containerError.value = error instanceof Error ? error.message : '容器化服务数据同步失败'
|
||||
} finally {
|
||||
containerLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => businessLineStore.current?.id,
|
||||
() => {
|
||||
loadContainerServices()
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
Reference in New Issue
Block a user