Files
xinfra/frontend/src/views/resource/Management.vue
T
wonder 46419bd57c feat(frontend): 添加资源管理页面骨架
页面:
- views/resource/StatusBoard.vue,资源状态看板(物理机/虚机/基础服务状态汇总)
- views/resource/Management.vue,资源管理(CMDB 物理机/虚机台账)

功能:
- 统计卡片展示
- 状态表格(按机房、业务线筛选)
- 进度条展示(CPU 使用率、健康率)

数据来源:SINA CMDB、阿里云同步、AWS 同步、七牛 LAS 同步
2026-07-14 15:40:04 +08:00

361 lines
9.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div>
<div class="page-head">
<div>
<h1>资源管理</h1>
<p>统一纳管物理机与虚机资源 · 基础数据来自 SINA CMDB</p>
</div>
<el-button type="primary">+ 创建虚机</el-button>
</div>
<div class="stat-row">
<div class="stat-card">
<div class="label">资源总数</div>
<div class="value">658</div>
<div class="delta">物理机 146 · 虚机 512</div>
</div>
<div class="stat-card">
<div class="label">SINA CMDB</div>
<div class="value">368</div>
<div class="delta">物理机 146 · 虚机 222</div>
</div>
<div class="stat-card">
<div class="label">阿里云同步</div>
<div class="value">154</div>
<div class="delta">ECS · 增量同步</div>
</div>
<div class="stat-card">
<div class="label">AWS / 七牛 LAS 同步</div>
<div class="value">136</div>
<div class="delta">AWS 58 · 七牛 LAS 78</div>
</div>
<div class="stat-card">
<div class="label">最近一次同步</div>
<div class="value" style="color: var(--accent); font-size: 16px">● 正常</div>
<div class="delta">07:39:10 · 新增 3 条</div>
</div>
</div>
<div class="las-toolbar">
<el-select placeholder="全部资源类型">
<el-option label="全部资源类型" value="" />
<el-option label="物理机" value="physical" />
<el-option label="虚机" value="vm" />
</el-select>
<el-select placeholder="全部数据来源">
<el-option label="全部数据来源" value="" />
<el-option label="SINA CMDB" value="cmdb" />
<el-option label="阿里云同步" value="aliyun" />
<el-option label="AWS 同步" value="aws" />
<el-option label="七牛 LAS 同步" value="qiniu" />
</el-select>
<el-select placeholder="全部业务线">
<el-option label="全部业务线" value="" />
<el-option label="kodo" value="kodo" />
<el-option label="las" value="las" />
<el-option label="lingxi" value="lingxi" />
<el-option label="ltoken" value="ltoken" />
<el-option label="maas" value="maas" />
<el-option label="未分配" value="unassigned" />
</el-select>
<el-input placeholder="搜索 hostname / asset_number / IP" style="flex: 1" />
</div>
<div class="panel">
<div class="panel-head">
<h3>资源台账 · 物理机 / 虚机</h3>
<span class="meta">SINA CMDB asset items + 云厂商同步增量</span>
</div>
<div class="panel-body">
<table>
<thead>
<tr>
<th>主机名</th>
<th>资产编号</th>
<th>类型</th>
<th>机房 / 区域</th>
<th>内网 IP</th>
<th>规格</th>
<th>业务线</th>
<th>数据来源</th>
<th>状态</th>
</tr>
</thead>
<tbody>
<tr v-for="item in resources" :key="item.hostname" class="tr-hover">
<td class="strong mono">{{ item.hostname }}</td>
<td class="mono" style="font-size: 11px">{{ item.asset_number }}</td>
<td><span class="tag" :class="item.type === '物理机' ? '' : 'vm'">{{ item.type }}</span></td>
<td><span class="tag zone-a">{{ item.location }}</span></td>
<td class="mono">{{ item.ip }}</td>
<td class="mono" style="font-size: 11px">{{ item.spec }}</td>
<td class="mono">{{ item.business_line }}</td>
<td><span class="tag src-cmdb">{{ item.source }}</span></td>
<td class="status-text ok">● {{ item.status }}</td>
</tr>
</tbody>
</table>
<div class="pagination">
<span>共 658 条 · 每页 7 条</span>
<div class="pg-btns">
<span class="pg-btn disabled">‹</span>
<span class="pg-btn active">1</span>
<span class="pg-btn">2</span>
<span class="pg-btn">3</span>
<span class="pg-sep">…</span>
<span class="pg-btn">94</span>
<span class="pg-btn">›</span>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const resources = ref([
{ hostname: 'xs291', asset_number: 'SERV00003502', type: '物理机', location: '华东·杭州下沙', ip: '10.34.37.52', spec: '20C/192G/13.52T', business_line: 'kodo', source: 'SINA CMDB', status: 'production' },
{ hostname: 'yzh-las-014', asset_number: 'SERV00004187', type: '虚机', location: '华北·YZH', ip: '10.21.4.214', spec: '32C/128G/4.0T', business_line: 'las', source: 'SINA CMDB', status: 'production' },
{ hostname: 'jf-bm-118', asset_number: 'SERV00004290', type: '物理机', location: '华南·JF', ip: '10.45.2.18', spec: '16C/64G/2.0T', business_line: 'lingxi', source: 'SINA CMDB', status: 'production' },
{ hostname: 'ali-ecs-sz-0231', asset_number: '—', type: '虚机', location: '阿里云·华南', ip: '172.18.4.31', spec: '16C/64G/500G', business_line: 'ltoken', source: '阿里云同步', status: 'production' },
{ hostname: 'aws-us-i-0a13fe2', asset_number: '—', type: '虚机', location: 'AWS · 美国', ip: '10.66.2.12', spec: '8C/32G/200G', business_line: 'maas', source: 'AWS 同步', status: 'production' },
{ hostname: 'sg-las-007', asset_number: 'SERV00004511', type: '虚机', location: '七牛 LAS · 新加坡', ip: '10.88.1.07', spec: '16C/64G/2.0T', business_line: 'las', source: '七牛 LAS 同步', status: 'idle' },
{ hostname: 'hk-bm-001', asset_number: 'SERV00004602', type: '物理机', location: '香港 IDC', ip: '10.90.0.11', spec: '8C/32G/1.0T', business_line: '未分配', source: 'SINA CMDB', status: 'production' },
])
</script>
<style scoped>
.page-head {
display: flex;
align-items: flex-end;
justify-content: space-between;
margin-bottom: 18px;
}
.page-head h1 {
font-size: 19px;
margin: 0 0 4px;
font-weight: 700;
}
.page-head p {
margin: 0;
color: var(--text-dim);
font-size: 12.5px;
}
.stat-row {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 12px;
margin-bottom: 20px;
}
.stat-card {
background: var(--bg-panel);
border: 1px solid var(--line);
border-radius: 8px;
padding: 14px 16px;
}
.stat-card .label {
font-size: 11px;
color: var(--text-dim);
margin-bottom: 8px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.stat-card .value {
font-family: var(--mono);
font-size: 24px;
font-weight: 600;
}
.stat-card .delta {
font-size: 11px;
color: var(--text-dim);
margin-top: 4px;
}
.stat-card .delta.up {
color: var(--accent);
}
.stat-card .delta.warn {
color: var(--warn);
}
.las-toolbar {
display: flex;
gap: 10px;
margin-bottom: 14px;
align-items: center;
}
.panel {
background: var(--bg-panel);
border: 1px solid var(--line);
border-radius: 8px;
}
.panel-head {
display: flex;
align-items: center;
justify-content: space-between;
padding: 13px 16px;
border-bottom: 1px solid var(--line-soft);
}
.panel-head h3 {
margin: 0;
font-size: 13.5px;
font-weight: 600;
}
.panel-head .meta {
font-size: 11.5px;
color: var(--text-dim);
font-family: var(--mono);
}
.panel-body {
padding: 4px 0;
}
table {
width: 100%;
border-collapse: collapse;
font-size: 12.5px;
}
th {
text-align: left;
color: var(--text-dim);
font-weight: 500;
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.4px;
padding: 9px 16px;
border-bottom: 1px solid var(--line-soft);
}
td {
padding: 11px 16px;
border-bottom: 1px solid var(--line-soft);
color: var(--text-mid);
}
tr:last-child td {
border-bottom: none;
}
tr.tr-hover:hover td {
background: var(--bg-panel-2);
}
.strong {
color: var(--text-hi);
font-weight: 500;
}
.mono {
font-family: var(--mono);
}
.tag {
display: inline-flex;
align-items: center;
gap: 5px;
font-family: var(--mono);
font-size: 10.5px;
padding: 2px 8px;
border-radius: 4px;
border: 1px solid var(--line);
color: var(--text-mid);
}
.tag.vm {
color: #8EC8FF;
border-color: #1C3A5C;
background: #0E1C2C;
}
.tag.zone-a {
color: #8EC8FF;
border-color: #1C3A5C;
background: #0E1C2C;
}
.tag.src-cmdb {
color: #7FFFC2;
border-color: var(--accent-dim);
background: #0F261D;
}
.status-text {
font-size: 12px;
display: flex;
align-items: center;
}
.status-text.ok {
color: var(--accent);
}
.pagination {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 16px;
border-top: 1px solid var(--line-soft);
font-size: 11.5px;
color: var(--text-dim);
}
.pg-btns {
display: flex;
gap: 4px;
align-items: center;
}
.pg-btn {
min-width: 26px;
height: 26px;
padding: 0 6px;
display: inline-flex;
align-items: center;
justify-content: center;
border: 1px solid var(--line);
border-radius: 5px;
color: var(--text-mid);
font-family: var(--mono);
font-size: 11px;
background: var(--bg-panel-2);
cursor: pointer;
}
.pg-btn.active {
background: var(--accent-dim);
border-color: var(--accent-dim);
color: #CFFCE9;
}
.pg-btn:hover {
border-color: #3A4356;
color: var(--text-hi);
}
.pg-btn.disabled {
opacity: 0.35;
cursor: not-allowed;
}
.pg-sep {
color: var(--text-dim);
padding: 0 2px;
}
</style>