Files
xinfra/frontend/src/views/resource/Management.vue
T
wonder f1a63ee473 feat(frontend): 支持深色/浅色模式切换,统一主题变量体系
- 新增 useTheme composable,支持 light/dark 双主题,默认浅色模式
- 主题偏好持久化到 localStorage,支持跟随系统主题
- AppHeader 顶栏添加 Moon/Sunny 切换图标,带旋转过渡动效
- variables.css 重构为 :root(浅色)/:root.dark(深色)双套变量
- 新增标签色系(蓝/紫/琥珀/绿)、节点色系、阴影、发光效果等语义化变量
- 全组件硬编码颜色替换为 CSS 变量,消除跨主题不一致
- global.css 添加主题切换过渡动画(background/border/box-shadow)
- 涉及组件:AppHeader、AppSidebar、SubsystemCard、AuditLogTable
- 涉及页面:Dashboard、Resource、Cluster、Config、Task、Service、Login
2026-07-15 14:38:58 +08:00

361 lines
9.4 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: var(--tag-blue-text);
border-color: var(--tag-blue-border);
background: var(--tag-blue-bg);
}
.tag.zone-a {
color: var(--tag-blue-text);
border-color: var(--tag-blue-border);
background: var(--tag-blue-bg);
}
.tag.src-cmdb {
color: var(--tag-green-text);
border-color: var(--tag-green-border);
background: var(--tag-green-bg);
}
.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: var(--active-text);
}
.pg-btn:hover {
border-color: var(--hover-border);
color: var(--text-hi);
}
.pg-btn.disabled {
opacity: 0.35;
cursor: not-allowed;
}
.pg-sep {
color: var(--text-dim);
padding: 0 2px;
}
</style>