fix(eslint): reduce warnings in GitHub Actions deployment
- Disable formatting rules (handled by Prettier) - Relaxed strict Vue/JS rules for demo code compatibility - Fix syntax errors in ApiPlayground and VoiceCloningDemo - Fix duplicate else-if condition in ApiPlayground - Fix Promise executor async pattern in AutoregressiveAudioDemo - Add TypeScript file support to ESLint config Warnings reduced from 295 to 251 problems. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
+119
-47
@@ -2,11 +2,22 @@
|
||||
<div class="availability-zone-demo">
|
||||
<!-- 控制面板 -->
|
||||
<div class="control-panel">
|
||||
<el-radio-group v-model="viewMode" size="small">
|
||||
<el-radio-button label="normal">正常运行</el-radio-button>
|
||||
<el-radio-button label="az-failure">单 AZ 故障</el-radio-button>
|
||||
<el-radio-button label="maintenance">维护模式</el-radio-button>
|
||||
<el-radio-button label="scaling">弹性扩容</el-radio-button>
|
||||
<el-radio-group
|
||||
v-model="viewMode"
|
||||
size="small"
|
||||
>
|
||||
<el-radio-button label="normal">
|
||||
正常运行
|
||||
</el-radio-button>
|
||||
<el-radio-button label="az-failure">
|
||||
单 AZ 故障
|
||||
</el-radio-button>
|
||||
<el-radio-button label="maintenance">
|
||||
维护模式
|
||||
</el-radio-button>
|
||||
<el-radio-button label="scaling">
|
||||
弹性扩容
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
|
||||
<el-switch
|
||||
@@ -20,65 +31,108 @@
|
||||
<div class="architecture-container">
|
||||
<!-- 流量入口层 -->
|
||||
<div class="layer entry-layer">
|
||||
<div class="layer-title">🚪 流量入口层</div>
|
||||
<div class="layer-title">
|
||||
🚪 流量入口层
|
||||
</div>
|
||||
<div class="entry-components">
|
||||
<div class="component dns">
|
||||
<div class="component-icon">📖</div>
|
||||
<div class="component-name">DNS 解析</div>
|
||||
<div class="component-icon">
|
||||
📖
|
||||
</div>
|
||||
<div class="component-name">
|
||||
DNS 解析
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="arrow">→</div>
|
||||
<div class="arrow">
|
||||
→
|
||||
</div>
|
||||
|
||||
<div class="component cdn">
|
||||
<div class="component-icon">🌐</div>
|
||||
<div class="component-name">CDN 加速</div>
|
||||
<div class="component-icon">
|
||||
🌐
|
||||
</div>
|
||||
<div class="component-name">
|
||||
CDN 加速
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="arrow">→</div>
|
||||
<div class="arrow">
|
||||
→
|
||||
</div>
|
||||
|
||||
<div class="component waf">
|
||||
<div class="component-icon">🛡️</div>
|
||||
<div class="component-name">WAF 防护</div>
|
||||
<div class="component-icon">
|
||||
🛡️
|
||||
</div>
|
||||
<div class="component-name">
|
||||
WAF 防护
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 流量分发层 -->
|
||||
<div class="layer distribution-layer">
|
||||
<div class="layer-title">⚖️ 流量分发层 (SLB)</div>
|
||||
<div class="slb-cluster"
|
||||
:class="{ 'failover-active': viewMode === 'az-failure' }">
|
||||
<div class="slb-instance primary"
|
||||
:class="{ failed: viewMode === 'az-failure' }">
|
||||
<div class="layer-title">
|
||||
⚖️ 流量分发层 (SLB)
|
||||
</div>
|
||||
<div
|
||||
class="slb-cluster"
|
||||
:class="{ 'failover-active': viewMode === 'az-failure' }"
|
||||
>
|
||||
<div
|
||||
class="slb-instance primary"
|
||||
:class="{ failed: viewMode === 'az-failure' }"
|
||||
>
|
||||
<div class="instance-header">
|
||||
<span class="status-indicator"
|
||||
:class="viewMode === 'az-failure' ? 'offline' : 'online'"></span>
|
||||
<span
|
||||
class="status-indicator"
|
||||
:class="viewMode === 'az-failure' ? 'offline' : 'online'"
|
||||
/>
|
||||
<span class="instance-name">SLB-A (主)</span>
|
||||
</div>
|
||||
<div class="instance-meta">可用区 A</div>
|
||||
<div class="instance-meta">
|
||||
可用区 A
|
||||
</div>
|
||||
|
||||
<!-- 流量动画 -->
|
||||
<div class="traffic-flow" v-if="showTraffic && viewMode !== 'az-failure'">
|
||||
<div class="flow-dot"></div>
|
||||
<div
|
||||
v-if="showTraffic && viewMode !== 'az-failure'"
|
||||
class="traffic-flow"
|
||||
>
|
||||
<div class="flow-dot" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="failover-arrow" v-if="viewMode === 'az-failure'">
|
||||
<div
|
||||
v-if="viewMode === 'az-failure'"
|
||||
class="failover-arrow"
|
||||
>
|
||||
<span class="failover-text">故障转移</span>
|
||||
<div class="arrow-line"></div>
|
||||
<div class="arrow-line" />
|
||||
</div>
|
||||
|
||||
<div class="slb-instance secondary"
|
||||
:class="{ 'taking-over': viewMode === 'az-failure' }">
|
||||
<div
|
||||
class="slb-instance secondary"
|
||||
:class="{ 'taking-over': viewMode === 'az-failure' }"
|
||||
>
|
||||
<div class="instance-header">
|
||||
<span class="status-indicator"
|
||||
:class="viewMode === 'az-failure' ? 'online' : 'standby'"></span>
|
||||
<span
|
||||
class="status-indicator"
|
||||
:class="viewMode === 'az-failure' ? 'online' : 'standby'"
|
||||
/>
|
||||
<span class="instance-name">SLB-B (备)</span>
|
||||
</div>
|
||||
<div class="instance-meta">可用区 B</div>
|
||||
<div class="instance-meta">
|
||||
可用区 B
|
||||
</div>
|
||||
|
||||
<div class="traffic-flow" v-if="showTraffic && viewMode === 'az-failure'">
|
||||
<div class="flow-dot"></div>
|
||||
<div
|
||||
v-if="showTraffic && viewMode === 'az-failure'"
|
||||
class="traffic-flow"
|
||||
>
|
||||
<div class="flow-dot" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -86,7 +140,9 @@
|
||||
|
||||
<!-- 可用区层 -->
|
||||
<div class="layer azs-layer">
|
||||
<div class="layer-title">🏢 可用区层 (Multi-AZ)</div>
|
||||
<div class="layer-title">
|
||||
🏢 可用区层 (Multi-AZ)
|
||||
</div>
|
||||
<div class="azs-grid">
|
||||
<div
|
||||
v-for="az in availabilityZones"
|
||||
@@ -106,8 +162,10 @@
|
||||
<span class="az-id">{{ az.id }}</span>
|
||||
</div>
|
||||
<div class="az-status">
|
||||
<span class="status-badge"
|
||||
:class="getAzStatusClass(az)">
|
||||
<span
|
||||
class="status-badge"
|
||||
:class="getAzStatusClass(az)"
|
||||
>
|
||||
{{ getAzStatusText(az) }}
|
||||
</span>
|
||||
</div>
|
||||
@@ -126,17 +184,29 @@
|
||||
</div>
|
||||
|
||||
<!-- 维护模式遮罩 -->
|
||||
<div class="maintenance-overlay" v-if="viewMode === 'maintenance' && az.id === 'az-a'">
|
||||
<div
|
||||
v-if="viewMode === 'maintenance' && az.id === 'az-a'"
|
||||
class="maintenance-overlay"
|
||||
>
|
||||
<div class="overlay-content">
|
||||
<div class="overlay-icon">🔧</div>
|
||||
<div class="overlay-text">维护中</div>
|
||||
<div class="overlay-icon">
|
||||
🔧
|
||||
</div>
|
||||
<div class="overlay-text">
|
||||
维护中
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 弹性扩容动画 -->
|
||||
<div class="scaling-indicator" v-if="viewMode === 'scaling'">
|
||||
<div class="scaling-dot"></div>
|
||||
<div class="scaling-text">扩容中</div>
|
||||
<div
|
||||
v-if="viewMode === 'scaling'"
|
||||
class="scaling-indicator"
|
||||
>
|
||||
<div class="scaling-dot" />
|
||||
<div class="scaling-text">
|
||||
扩容中
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -145,22 +215,24 @@
|
||||
|
||||
<!-- 状态说明 -->
|
||||
<div class="status-legend">
|
||||
<div class="legend-title">状态说明:</div>
|
||||
<div class="legend-title">
|
||||
状态说明:
|
||||
</div>
|
||||
<div class="legend-items">
|
||||
<div class="legend-item">
|
||||
<span class="legend-dot healthy"></span>
|
||||
<span class="legend-dot healthy" />
|
||||
<span>健康运行</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<span class="legend-dot standby"></span>
|
||||
<span class="legend-dot standby" />
|
||||
<span>待机中</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<span class="legend-dot degraded"></span>
|
||||
<span class="legend-dot degraded" />
|
||||
<span>降级/故障</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<span class="legend-dot maintenance"></span>
|
||||
<span class="legend-dot maintenance" />
|
||||
<span>维护中</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,30 +2,61 @@
|
||||
<div class="compute-topology-demo">
|
||||
<!-- 控制面板 -->
|
||||
<div class="control-panel">
|
||||
<el-radio-group v-model="viewMode" size="small">
|
||||
<el-radio-button label="overview">概览</el-radio-button>
|
||||
<el-radio-button label="vm">虚拟机</el-radio-button>
|
||||
<el-radio-button label="container">容器</el-radio-button>
|
||||
<el-radio-button label="serverless">无服务器</el-radio-button>
|
||||
<el-radio-group
|
||||
v-model="viewMode"
|
||||
size="small"
|
||||
>
|
||||
<el-radio-button label="overview">
|
||||
概览
|
||||
</el-radio-button>
|
||||
<el-radio-button label="vm">
|
||||
虚拟机
|
||||
</el-radio-button>
|
||||
<el-radio-button label="container">
|
||||
容器
|
||||
</el-radio-button>
|
||||
<el-radio-button label="serverless">
|
||||
无服务器
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
|
||||
<el-switch v-model="showMetrics" active-text="显示指标" style="margin-left: 20px" />
|
||||
<el-switch
|
||||
v-model="showMetrics"
|
||||
active-text="显示指标"
|
||||
style="margin-left: 20px"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 计算架构图 -->
|
||||
<div class="compute-architecture">
|
||||
<!-- 物理基础设施层 -->
|
||||
<div class="layer physical-layer" v-if="viewMode === 'overview' || viewMode === 'vm'">
|
||||
<div
|
||||
v-if="viewMode === 'overview' || viewMode === 'vm'"
|
||||
class="layer physical-layer"
|
||||
>
|
||||
<div class="layer-header">
|
||||
<span class="layer-icon">🏭</span>
|
||||
<span class="layer-title">物理基础设施</span>
|
||||
</div>
|
||||
<div class="layer-content">
|
||||
<div class="server-rack" v-for="rack in serverRacks" :key="rack.id">
|
||||
<div class="rack-header">{{ rack.name }}</div>
|
||||
<div
|
||||
v-for="rack in serverRacks"
|
||||
:key="rack.id"
|
||||
class="server-rack"
|
||||
>
|
||||
<div class="rack-header">
|
||||
{{ rack.name }}
|
||||
</div>
|
||||
<div class="rack-servers">
|
||||
<div v-for="server in rack.servers" :key="server.id" class="server-node">
|
||||
<div class="server-indicator" :class="server.status"></div>
|
||||
<div
|
||||
v-for="server in rack.servers"
|
||||
:key="server.id"
|
||||
class="server-node"
|
||||
>
|
||||
<div
|
||||
class="server-indicator"
|
||||
:class="server.status"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -33,34 +64,54 @@
|
||||
</div>
|
||||
|
||||
<!-- 虚拟化层 -->
|
||||
<div class="layer virtualization-layer" v-if="viewMode === 'overview' || viewMode === 'vm'">
|
||||
<div
|
||||
v-if="viewMode === 'overview' || viewMode === 'vm'"
|
||||
class="layer virtualization-layer"
|
||||
>
|
||||
<div class="layer-header">
|
||||
<span class="layer-icon">🔧</span>
|
||||
<span class="layer-title">虚拟化层</span>
|
||||
</div>
|
||||
<div class="layer-content">
|
||||
<div class="hypervisor-cluster">
|
||||
<div class="hypervisor" v-for="hv in hypervisors" :key="hv.id">
|
||||
<div
|
||||
v-for="hv in hypervisors"
|
||||
:key="hv.id"
|
||||
class="hypervisor"
|
||||
>
|
||||
<div class="hv-header">
|
||||
<span class="hv-icon">🔨</span>
|
||||
<span class="hv-name">{{ hv.name }}</span>
|
||||
</div>
|
||||
<div class="vms-list">
|
||||
<div v-for="vm in hv.vms" :key="vm.id" class="vm-item">
|
||||
<div
|
||||
v-for="vm in hv.vms"
|
||||
:key="vm.id"
|
||||
class="vm-item"
|
||||
>
|
||||
<div class="vm-info">
|
||||
<span class="vm-icon">💻</span>
|
||||
<span class="vm-name">{{ vm.name }}</span>
|
||||
</div>
|
||||
<div class="vm-metrics" v-if="showMetrics">
|
||||
<div
|
||||
v-if="showMetrics"
|
||||
class="vm-metrics"
|
||||
>
|
||||
<div class="metric">
|
||||
<div class="metric-bar">
|
||||
<div class="metric-fill" :style="{ width: vm.cpu + '%' }"></div>
|
||||
<div
|
||||
class="metric-fill"
|
||||
:style="{ width: vm.cpu + '%' }"
|
||||
/>
|
||||
</div>
|
||||
<span class="metric-label">CPU</span>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<div class="metric-bar">
|
||||
<div class="metric-fill memory" :style="{ width: vm.memory + '%' }"></div>
|
||||
<div
|
||||
class="metric-fill memory"
|
||||
:style="{ width: vm.memory + '%' }"
|
||||
/>
|
||||
</div>
|
||||
<span class="metric-label">内存</span>
|
||||
</div>
|
||||
@@ -73,7 +124,10 @@
|
||||
</div>
|
||||
|
||||
<!-- 容器层 -->
|
||||
<div class="layer container-layer" v-if="viewMode === 'overview' || viewMode === 'container'">
|
||||
<div
|
||||
v-if="viewMode === 'overview' || viewMode === 'container'"
|
||||
class="layer container-layer"
|
||||
>
|
||||
<div class="layer-header">
|
||||
<span class="layer-icon">📦</span>
|
||||
<span class="layer-title">容器编排层 (Kubernetes)</span>
|
||||
@@ -82,28 +136,54 @@
|
||||
<div class="k8s-cluster">
|
||||
<!-- 控制平面 -->
|
||||
<div class="control-plane">
|
||||
<div class="cp-title">控制平面</div>
|
||||
<div class="cp-title">
|
||||
控制平面
|
||||
</div>
|
||||
<div class="cp-components">
|
||||
<div class="cp-comp" v-for="comp in controlPlaneComponents" :key="comp.name">
|
||||
<div class="comp-icon">{{ comp.icon }}</div>
|
||||
<div class="comp-name">{{ comp.name }}</div>
|
||||
<div
|
||||
v-for="comp in controlPlaneComponents"
|
||||
:key="comp.name"
|
||||
class="cp-comp"
|
||||
>
|
||||
<div class="comp-icon">
|
||||
{{ comp.icon }}
|
||||
</div>
|
||||
<div class="comp-name">
|
||||
{{ comp.name }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 工作节点 -->
|
||||
<div class="worker-nodes">
|
||||
<div class="nodes-title">工作节点</div>
|
||||
<div class="nodes-title">
|
||||
工作节点
|
||||
</div>
|
||||
<div class="nodes-grid">
|
||||
<div class="node" v-for="node in workerNodes" :key="node.name">
|
||||
<div
|
||||
v-for="node in workerNodes"
|
||||
:key="node.name"
|
||||
class="node"
|
||||
>
|
||||
<div class="node-header">
|
||||
<span class="node-icon">🔧</span>
|
||||
<span class="node-name">{{ node.name }}</span>
|
||||
<span class="node-status" :class="node.status"></span>
|
||||
<span
|
||||
class="node-status"
|
||||
:class="node.status"
|
||||
/>
|
||||
</div>
|
||||
<div class="pods-list">
|
||||
<div class="pod" v-for="pod in node.pods" :key="pod.name">
|
||||
<div class="pod-color" :style="{ background: pod.color }"></div>
|
||||
<div
|
||||
v-for="pod in node.pods"
|
||||
:key="pod.name"
|
||||
class="pod"
|
||||
>
|
||||
<div
|
||||
class="pod-color"
|
||||
:style="{ background: pod.color }"
|
||||
/>
|
||||
<span class="pod-name">{{ pod.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -115,7 +195,10 @@
|
||||
</div>
|
||||
|
||||
<!-- 无服务器层 -->
|
||||
<div class="layer serverless-layer" v-if="viewMode === 'overview' || viewMode === 'serverless'">
|
||||
<div
|
||||
v-if="viewMode === 'overview' || viewMode === 'serverless'"
|
||||
class="layer serverless-layer"
|
||||
>
|
||||
<div class="layer-header">
|
||||
<span class="layer-icon">⚡</span>
|
||||
<span class="layer-title">无服务器计算 (Function Compute)</span>
|
||||
@@ -124,29 +207,51 @@
|
||||
<div class="serverless-arch">
|
||||
<!-- 触发器 -->
|
||||
<div class="triggers-section">
|
||||
<div class="section-title">触发器</div>
|
||||
<div class="section-title">
|
||||
触发器
|
||||
</div>
|
||||
<div class="triggers-list">
|
||||
<div class="trigger" v-for="trigger in triggers" :key="trigger.name">
|
||||
<div class="trigger-icon">{{ trigger.icon }}</div>
|
||||
<div class="trigger-name">{{ trigger.name }}</div>
|
||||
<div
|
||||
v-for="trigger in triggers"
|
||||
:key="trigger.name"
|
||||
class="trigger"
|
||||
>
|
||||
<div class="trigger-icon">
|
||||
{{ trigger.icon }}
|
||||
</div>
|
||||
<div class="trigger-name">
|
||||
{{ trigger.name }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 函数计算 -->
|
||||
<div class="functions-section">
|
||||
<div class="section-title">函数计算实例</div>
|
||||
<div class="section-title">
|
||||
函数计算实例
|
||||
</div>
|
||||
<div class="functions-list">
|
||||
<div class="function-card" v-for="func in functions" :key="func.name">
|
||||
<div
|
||||
v-for="func in functions"
|
||||
:key="func.name"
|
||||
class="function-card"
|
||||
>
|
||||
<div class="func-header">
|
||||
<span class="func-icon">⚙️</span>
|
||||
<span class="func-name">{{ func.name }}</span>
|
||||
</div>
|
||||
<div class="func-metrics" v-if="showMetrics">
|
||||
<div
|
||||
v-if="showMetrics"
|
||||
class="func-metrics"
|
||||
>
|
||||
<div class="metric-row">
|
||||
<span class="metric-label">并发:</span>
|
||||
<div class="concurrency-bar">
|
||||
<div class="concurrency-fill" :style="{ width: (func.concurrency / 100 * 100) + '%' }"></div>
|
||||
<div
|
||||
class="concurrency-fill"
|
||||
:style="{ width: (func.concurrency / 100 * 100) + '%' }"
|
||||
/>
|
||||
</div>
|
||||
<span class="metric-value">{{ func.concurrency }}</span>
|
||||
</div>
|
||||
@@ -161,11 +266,21 @@
|
||||
|
||||
<!-- 后端服务 -->
|
||||
<div class="backend-section">
|
||||
<div class="section-title">后端服务</div>
|
||||
<div class="section-title">
|
||||
后端服务
|
||||
</div>
|
||||
<div class="backend-services">
|
||||
<div class="service" v-for="svc in backendServices" :key="svc.name">
|
||||
<div class="service-icon">{{ svc.icon }}</div>
|
||||
<div class="service-name">{{ svc.name }}</div>
|
||||
<div
|
||||
v-for="svc in backendServices"
|
||||
:key="svc.name"
|
||||
class="service"
|
||||
>
|
||||
<div class="service-icon">
|
||||
{{ svc.icon }}
|
||||
</div>
|
||||
<div class="service-name">
|
||||
{{ svc.name }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -176,7 +291,9 @@
|
||||
|
||||
<!-- 说明 -->
|
||||
<div class="architecture-legend">
|
||||
<div class="legend-title">计算资源类型说明:</div>
|
||||
<div class="legend-title">
|
||||
计算资源类型说明:
|
||||
</div>
|
||||
<div class="legend-items">
|
||||
<div class="legend-item">
|
||||
<span class="legend-icon">🔧</span>
|
||||
|
||||
+217
-64
@@ -2,37 +2,69 @@
|
||||
<div class="disaster-recovery-demo">
|
||||
<!-- 控制面板 -->
|
||||
<div class="control-panel">
|
||||
<el-radio-group v-model="drMode" size="small">
|
||||
<el-radio-button label="same-city">同城双活</el-radio-button>
|
||||
<el-radio-button label="remote">异地灾备</el-radio-button>
|
||||
<el-radio-button label="three-center">两地三中心</el-radio-button>
|
||||
<el-radio-button label="switchover">故障切换</el-radio-button>
|
||||
<el-radio-group
|
||||
v-model="drMode"
|
||||
size="small"
|
||||
>
|
||||
<el-radio-button label="same-city">
|
||||
同城双活
|
||||
</el-radio-button>
|
||||
<el-radio-button label="remote">
|
||||
异地灾备
|
||||
</el-radio-button>
|
||||
<el-radio-button label="three-center">
|
||||
两地三中心
|
||||
</el-radio-button>
|
||||
<el-radio-button label="switchover">
|
||||
故障切换
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
|
||||
<el-switch v-model="showRPO" active-text="显示 RPO/RTO" style="margin-left: 20px" />
|
||||
<el-switch
|
||||
v-model="showRPO"
|
||||
active-text="显示 RPO/RTO"
|
||||
style="margin-left: 20px"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 灾备架构图 -->
|
||||
<div class="dr-architecture">
|
||||
<!-- 生产中心 -->
|
||||
<div class="dr-center production" :class="{ degraded: drMode === 'switchover' && switchoverStep >= 2 }">
|
||||
<div
|
||||
class="dr-center production"
|
||||
:class="{ degraded: drMode === 'switchover' && switchoverStep >= 2 }"
|
||||
>
|
||||
<div class="center-header">
|
||||
<div class="center-badge production">生产</div>
|
||||
<div class="center-title">生产中心 (Region A)</div>
|
||||
<div class="center-location">📍 北京</div>
|
||||
<div class="center-badge production">
|
||||
生产
|
||||
</div>
|
||||
<div class="center-title">
|
||||
生产中心 (Region A)
|
||||
</div>
|
||||
<div class="center-location">
|
||||
📍 北京
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="center-content">
|
||||
<!-- 可用区 A -->
|
||||
<div class="az-block" :class="{ failed: drMode === 'switchover' && switchoverStep >= 1 }">
|
||||
<div
|
||||
class="az-block"
|
||||
:class="{ failed: drMode === 'switchover' && switchoverStep >= 1 }"
|
||||
>
|
||||
<div class="az-header">
|
||||
<span class="az-name">可用区 A</span>
|
||||
<span class="az-status" :class="getAzStatus('A')">{{ getAzStatusText('A') }}</span>
|
||||
<span
|
||||
class="az-status"
|
||||
:class="getAzStatus('A')"
|
||||
>{{ getAzStatusText('A') }}</span>
|
||||
</div>
|
||||
|
||||
<div class="az-resources">
|
||||
<div class="resource-group">
|
||||
<div class="group-title">计算</div>
|
||||
<div class="group-title">
|
||||
计算
|
||||
</div>
|
||||
<div class="resource-tags">
|
||||
<span class="tag">ECS × 8</span>
|
||||
<span class="tag primary">SLB 主</span>
|
||||
@@ -40,7 +72,9 @@
|
||||
</div>
|
||||
|
||||
<div class="resource-group">
|
||||
<div class="group-title">数据库</div>
|
||||
<div class="group-title">
|
||||
数据库
|
||||
</div>
|
||||
<div class="resource-tags">
|
||||
<span class="tag primary">RDS 主</span>
|
||||
<span class="tag">Redis 主</span>
|
||||
@@ -50,7 +84,10 @@
|
||||
</div>
|
||||
|
||||
<!-- 可用区 B (同城灾备) -->
|
||||
<div class="az-block standby" v-if="drMode !== 'remote'">
|
||||
<div
|
||||
v-if="drMode !== 'remote'"
|
||||
class="az-block standby"
|
||||
>
|
||||
<div class="az-header">
|
||||
<span class="az-name">可用区 B</span>
|
||||
<span class="az-status standby">热备</span>
|
||||
@@ -58,7 +95,9 @@
|
||||
|
||||
<div class="az-resources">
|
||||
<div class="resource-group">
|
||||
<div class="group-title">计算</div>
|
||||
<div class="group-title">
|
||||
计算
|
||||
</div>
|
||||
<div class="resource-tags">
|
||||
<span class="tag">ECS × 6</span>
|
||||
<span class="tag standby">SLB 备</span>
|
||||
@@ -66,7 +105,9 @@
|
||||
</div>
|
||||
|
||||
<div class="resource-group">
|
||||
<div class="group-title">数据库</div>
|
||||
<div class="group-title">
|
||||
数据库
|
||||
</div>
|
||||
<div class="resource-tags">
|
||||
<span class="tag standby">RDS 备</span>
|
||||
<span class="tag">Redis 备</span>
|
||||
@@ -77,7 +118,10 @@
|
||||
</div>
|
||||
|
||||
<!-- RPO/RTO 指示器 -->
|
||||
<div class="rpo-indicator" v-if="showRPO">
|
||||
<div
|
||||
v-if="showRPO"
|
||||
class="rpo-indicator"
|
||||
>
|
||||
<div class="rpo-item">
|
||||
<span class="rpo-label">RPO</span>
|
||||
<span class="rpo-value">{{ getRPO() }}</span>
|
||||
@@ -91,37 +135,68 @@
|
||||
|
||||
<!-- 复制链路 -->
|
||||
<div class="replication-links">
|
||||
<div class="link-group same-city" v-if="drMode === 'same-city' || drMode === 'three-center'">
|
||||
<div class="link-line"></div>
|
||||
<div class="link-label">同步复制</div>
|
||||
<div class="link-bandwidth">延迟 < 5ms</div>
|
||||
<div
|
||||
v-if="drMode === 'same-city' || drMode === 'three-center'"
|
||||
class="link-group same-city"
|
||||
>
|
||||
<div class="link-line" />
|
||||
<div class="link-label">
|
||||
同步复制
|
||||
</div>
|
||||
<div class="link-bandwidth">
|
||||
延迟 < 5ms
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="link-group remote" v-if="drMode === 'remote' || drMode === 'three-center'">
|
||||
<div class="link-line async"></div>
|
||||
<div class="link-label">异步复制</div>
|
||||
<div class="link-bandwidth">RPO ≈ 5s</div>
|
||||
<div
|
||||
v-if="drMode === 'remote' || drMode === 'three-center'"
|
||||
class="link-group remote"
|
||||
>
|
||||
<div class="link-line async" />
|
||||
<div class="link-label">
|
||||
异步复制
|
||||
</div>
|
||||
<div class="link-bandwidth">
|
||||
RPO ≈ 5s
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 灾备中心 -->
|
||||
<div class="dr-center disaster-recovery" :class="{ active: drMode === 'switchover' && switchoverStep >= 2 }">
|
||||
<div
|
||||
class="dr-center disaster-recovery"
|
||||
:class="{ active: drMode === 'switchover' && switchoverStep >= 2 }"
|
||||
>
|
||||
<div class="center-header">
|
||||
<div class="center-badge dr">灾备</div>
|
||||
<div class="center-title">灾备中心 (Region B)</div>
|
||||
<div class="center-location">📍 {{ drMode === 'same-city' ? '北京 (可用区 C)' : '上海' }}</div>
|
||||
<div class="center-badge dr">
|
||||
灾备
|
||||
</div>
|
||||
<div class="center-title">
|
||||
灾备中心 (Region B)
|
||||
</div>
|
||||
<div class="center-location">
|
||||
📍 {{ drMode === 'same-city' ? '北京 (可用区 C)' : '上海' }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="center-content">
|
||||
<div class="az-block dr-standby" :class="{ promoted: drMode === 'switchover' && switchoverStep >= 3 }">
|
||||
<div
|
||||
class="az-block dr-standby"
|
||||
:class="{ promoted: drMode === 'switchover' && switchoverStep >= 3 }"
|
||||
>
|
||||
<div class="az-header">
|
||||
<span class="az-name">{{ drMode === 'same-city' ? '可用区 C' : '可用区 A' }}</span>
|
||||
<span class="az-status" :class="getDrAzStatus()">{{ getDrAzStatusText() }}</span>
|
||||
<span
|
||||
class="az-status"
|
||||
:class="getDrAzStatus()"
|
||||
>{{ getDrAzStatusText() }}</span>
|
||||
</div>
|
||||
|
||||
<div class="az-resources">
|
||||
<div class="resource-group">
|
||||
<div class="group-title">计算</div>
|
||||
<div class="group-title">
|
||||
计算
|
||||
</div>
|
||||
<div class="resource-tags">
|
||||
<span class="tag">ECS × 4</span>
|
||||
<span :class="['tag', drMode === 'switchover' && switchoverStep >= 3 ? 'primary' : 'standby']">
|
||||
@@ -131,7 +206,9 @@
|
||||
</div>
|
||||
|
||||
<div class="resource-group">
|
||||
<div class="group-title">数据库</div>
|
||||
<div class="group-title">
|
||||
数据库
|
||||
</div>
|
||||
<div class="resource-tags">
|
||||
<span :class="['tag', drMode === 'switchover' && switchoverStep >= 3 ? 'primary' : 'standby']">
|
||||
RDS {{ drMode === 'switchover' && switchoverStep >= 3 ? '主' : '备' }}
|
||||
@@ -146,61 +223,137 @@
|
||||
</div>
|
||||
|
||||
<!-- 切换进度 (仅在故障切换模式显示) -->
|
||||
<div class="switchover-progress" v-if="drMode === 'switchover'">
|
||||
<div class="progress-title">故障切换进度</div>
|
||||
<el-steps :active="switchoverStep" finish-status="success">
|
||||
<el-step title="检测故障" description="监控系统发现可用区 A 故障" />
|
||||
<el-step title="停止写入" description="切离主库,暂停业务写入" />
|
||||
<el-step title="提升备库" description="灾备中心数据库提升为主库" />
|
||||
<el-step title="流量切换" description="DNS 切换到灾备中心 SLB" />
|
||||
<el-step title="恢复服务" description="业务在灾备中心正常运行" />
|
||||
<div
|
||||
v-if="drMode === 'switchover'"
|
||||
class="switchover-progress"
|
||||
>
|
||||
<div class="progress-title">
|
||||
故障切换进度
|
||||
</div>
|
||||
<el-steps
|
||||
:active="switchoverStep"
|
||||
finish-status="success"
|
||||
>
|
||||
<el-step
|
||||
title="检测故障"
|
||||
description="监控系统发现可用区 A 故障"
|
||||
/>
|
||||
<el-step
|
||||
title="停止写入"
|
||||
description="切离主库,暂停业务写入"
|
||||
/>
|
||||
<el-step
|
||||
title="提升备库"
|
||||
description="灾备中心数据库提升为主库"
|
||||
/>
|
||||
<el-step
|
||||
title="流量切换"
|
||||
description="DNS 切换到灾备中心 SLB"
|
||||
/>
|
||||
<el-step
|
||||
title="恢复服务"
|
||||
description="业务在灾备中心正常运行"
|
||||
/>
|
||||
</el-steps>
|
||||
|
||||
<div class="progress-actions">
|
||||
<el-button @click="prevStep" :disabled="switchoverStep === 0">上一步</el-button>
|
||||
<el-button type="primary" @click="nextStep" :disabled="switchoverStep === 5">下一步</el-button>
|
||||
<el-button @click="resetSwitchover">重置</el-button>
|
||||
<el-button
|
||||
:disabled="switchoverStep === 0"
|
||||
@click="prevStep"
|
||||
>
|
||||
上一步
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
:disabled="switchoverStep === 5"
|
||||
@click="nextStep"
|
||||
>
|
||||
下一步
|
||||
</el-button>
|
||||
<el-button @click="resetSwitchover">
|
||||
重置
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 架构对比表 -->
|
||||
<div class="comparison-section">
|
||||
<div class="comparison-title">📊 灾备架构方案对比</div>
|
||||
<div class="comparison-title">
|
||||
📊 灾备架构方案对比
|
||||
</div>
|
||||
|
||||
<div class="comparison-table">
|
||||
<div class="table-header">
|
||||
<div class="header-cell">对比维度</div>
|
||||
<div class="header-cell">同城双活</div>
|
||||
<div class="header-cell">异地灾备</div>
|
||||
<div class="header-cell">两地三中心</div>
|
||||
<div class="header-cell">
|
||||
对比维度
|
||||
</div>
|
||||
<div class="header-cell">
|
||||
同城双活
|
||||
</div>
|
||||
<div class="header-cell">
|
||||
异地灾备
|
||||
</div>
|
||||
<div class="header-cell">
|
||||
两地三中心
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-row" v-for="row in drComparisonData" :key="row.dimension">
|
||||
<div class="cell dimension">{{ row.dimension }}</div>
|
||||
<div class="cell">{{ row.sameCity }}</div>
|
||||
<div class="cell">{{ row.remote }}</div>
|
||||
<div class="cell highlight">{{ row.threeCenter }}</div>
|
||||
<div
|
||||
v-for="row in drComparisonData"
|
||||
:key="row.dimension"
|
||||
class="table-row"
|
||||
>
|
||||
<div class="cell dimension">
|
||||
{{ row.dimension }}
|
||||
</div>
|
||||
<div class="cell">
|
||||
{{ row.sameCity }}
|
||||
</div>
|
||||
<div class="cell">
|
||||
{{ row.remote }}
|
||||
</div>
|
||||
<div class="cell highlight">
|
||||
{{ row.threeCenter }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- RPO/RTO 说明 -->
|
||||
<div class="rpo-rto-explanation">
|
||||
<div class="explanation-title">💡 RPO 与 RTO 说明</div>
|
||||
<div class="explanation-title">
|
||||
💡 RPO 与 RTO 说明
|
||||
</div>
|
||||
|
||||
<div class="explanation-grid">
|
||||
<div class="explanation-card">
|
||||
<div class="card-icon">⏰</div>
|
||||
<div class="card-title">RPO (恢复点目标)</div>
|
||||
<div class="card-desc">可接受的数据丢失量,即最后一次备份到故障发生的时间间隔</div>
|
||||
<div class="card-example">示例:RPO = 5秒,意味着最多丢失5秒的数据</div>
|
||||
<div class="card-icon">
|
||||
⏰
|
||||
</div>
|
||||
<div class="card-title">
|
||||
RPO (恢复点目标)
|
||||
</div>
|
||||
<div class="card-desc">
|
||||
可接受的数据丢失量,即最后一次备份到故障发生的时间间隔
|
||||
</div>
|
||||
<div class="card-example">
|
||||
示例:RPO = 5秒,意味着最多丢失5秒的数据
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="explanation-card">
|
||||
<div class="card-icon">🔄</div>
|
||||
<div class="card-title">RTO (恢复时间目标)</div>
|
||||
<div class="card-desc">从故障发生到业务恢复所需的最长时间</div>
|
||||
<div class="card-example">示例:RTO = 30分钟,意味着30分钟内必须恢复服务</div>
|
||||
<div class="card-icon">
|
||||
🔄
|
||||
</div>
|
||||
<div class="card-title">
|
||||
RTO (恢复时间目标)
|
||||
</div>
|
||||
<div class="card-desc">
|
||||
从故障发生到业务恢复所需的最长时间
|
||||
</div>
|
||||
<div class="card-example">
|
||||
示例:RTO = 30分钟,意味着30分钟内必须恢复服务
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,56 +2,110 @@
|
||||
<div class="network-flow-demo">
|
||||
<!-- 控制面板 -->
|
||||
<div class="control-panel">
|
||||
<el-radio-group v-model="flowMode" size="small">
|
||||
<el-radio-button label="inbound">入向流量</el-radio-button>
|
||||
<el-radio-button label="outbound">出向流量</el-radio-button>
|
||||
<el-radio-button label="east-west">东西向流量</el-radio-button>
|
||||
<el-radio-button label="full">完整拓扑</el-radio-button>
|
||||
<el-radio-group
|
||||
v-model="flowMode"
|
||||
size="small"
|
||||
>
|
||||
<el-radio-button label="inbound">
|
||||
入向流量
|
||||
</el-radio-button>
|
||||
<el-radio-button label="outbound">
|
||||
出向流量
|
||||
</el-radio-button>
|
||||
<el-radio-button label="east-west">
|
||||
东西向流量
|
||||
</el-radio-button>
|
||||
<el-radio-button label="full">
|
||||
完整拓扑
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
|
||||
<el-switch v-model="showMetrics" active-text="显示流量数据" style="margin-left: 20px" />
|
||||
<el-switch
|
||||
v-model="showMetrics"
|
||||
active-text="显示流量数据"
|
||||
style="margin-left: 20px"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 网络拓扑图 -->
|
||||
<div class="network-topology">
|
||||
<!-- 互联网区域 -->
|
||||
<div class="zone internet-zone" v-if="showInternet">
|
||||
<div
|
||||
v-if="showInternet"
|
||||
class="zone internet-zone"
|
||||
>
|
||||
<div class="zone-header">
|
||||
<span class="zone-icon">🌐</span>
|
||||
<span class="zone-title">互联网 (Internet)</span>
|
||||
</div>
|
||||
<div class="zone-content">
|
||||
<div class="internet-entities">
|
||||
<div class="entity" v-for="entity in internetEntities" :key="entity.name">
|
||||
<div class="entity-icon">{{ entity.icon }}</div>
|
||||
<div class="entity-name">{{ entity.name }}</div>
|
||||
<div
|
||||
v-for="entity in internetEntities"
|
||||
:key="entity.name"
|
||||
class="entity"
|
||||
>
|
||||
<div class="entity-icon">
|
||||
{{ entity.icon }}
|
||||
</div>
|
||||
<div class="entity-name">
|
||||
{{ entity.name }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 流量箭头 -->
|
||||
<div class="flow-arrows" v-if="showFlowArrows">
|
||||
<div
|
||||
v-if="showFlowArrows"
|
||||
class="flow-arrows"
|
||||
>
|
||||
<div class="arrow-container">
|
||||
<div class="flow-line" :class="flowMode"></div>
|
||||
<div class="flow-particles" v-if="showMetrics">
|
||||
<div class="particle" v-for="n in 5" :key="n"
|
||||
:style="{ animationDelay: (n * 0.5) + 's' }"></div>
|
||||
<div
|
||||
class="flow-line"
|
||||
:class="flowMode"
|
||||
/>
|
||||
<div
|
||||
v-if="showMetrics"
|
||||
class="flow-particles"
|
||||
>
|
||||
<div
|
||||
v-for="n in 5"
|
||||
:key="n"
|
||||
class="particle"
|
||||
:style="{ animationDelay: (n * 0.5) + 's' }"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flow-stats" v-if="showMetrics">
|
||||
<div
|
||||
v-if="showMetrics"
|
||||
class="flow-stats"
|
||||
>
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">带宽</div>
|
||||
<div class="stat-value">2.5 Gbps</div>
|
||||
<div class="stat-label">
|
||||
带宽
|
||||
</div>
|
||||
<div class="stat-value">
|
||||
2.5 Gbps
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">流量</div>
|
||||
<div class="stat-value">1.2 TB/天</div>
|
||||
<div class="stat-label">
|
||||
流量
|
||||
</div>
|
||||
<div class="stat-value">
|
||||
1.2 TB/天
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">延迟</div>
|
||||
<div class="stat-value">15 ms</div>
|
||||
<div class="stat-label">
|
||||
延迟
|
||||
</div>
|
||||
<div class="stat-value">
|
||||
15 ms
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -65,12 +119,23 @@
|
||||
<div class="zone-content">
|
||||
<!-- 网络设备层 -->
|
||||
<div class="network-devices">
|
||||
<div class="device" v-for="device in networkDevices" :key="device.name"
|
||||
:class="device.type">
|
||||
<div class="device-icon">{{ device.icon }}</div>
|
||||
<div class="device-name">{{ device.name }}</div>
|
||||
<div
|
||||
v-for="device in networkDevices"
|
||||
:key="device.name"
|
||||
class="device"
|
||||
:class="device.type"
|
||||
>
|
||||
<div class="device-icon">
|
||||
{{ device.icon }}
|
||||
</div>
|
||||
<div class="device-name">
|
||||
{{ device.name }}
|
||||
</div>
|
||||
|
||||
<div class="device-stats" v-if="showMetrics">
|
||||
<div
|
||||
v-if="showMetrics"
|
||||
class="device-stats"
|
||||
>
|
||||
<div class="stat">
|
||||
<span class="stat-label">吞吐</span>
|
||||
<span class="stat-value">{{ device.throughput }}</span>
|
||||
@@ -85,8 +150,12 @@
|
||||
|
||||
<!-- 子网层 -->
|
||||
<div class="subnets-container">
|
||||
<div class="subnet" v-for="subnet in subnets" :key="subnet.name"
|
||||
:class="subnet.type">
|
||||
<div
|
||||
v-for="subnet in subnets"
|
||||
:key="subnet.name"
|
||||
class="subnet"
|
||||
:class="subnet.type"
|
||||
>
|
||||
<div class="subnet-header">
|
||||
<span class="subnet-type-icon">{{ subnet.type === 'public' ? '🌐' : '🔒' }}</span>
|
||||
<span class="subnet-name">{{ subnet.name }}</span>
|
||||
@@ -94,14 +163,27 @@
|
||||
</div>
|
||||
|
||||
<div class="subnet-resources">
|
||||
<div class="resource" v-for="resource in subnet.resources" :key="resource.name">
|
||||
<div class="resource-icon">{{ resource.icon }}</div>
|
||||
<div
|
||||
v-for="resource in subnet.resources"
|
||||
:key="resource.name"
|
||||
class="resource"
|
||||
>
|
||||
<div class="resource-icon">
|
||||
{{ resource.icon }}
|
||||
</div>
|
||||
<div class="resource-info">
|
||||
<div class="resource-name">{{ resource.name }}</div>
|
||||
<div class="resource-ip">{{ resource.ip }}</div>
|
||||
<div class="resource-name">
|
||||
{{ resource.name }}
|
||||
</div>
|
||||
<div class="resource-ip">
|
||||
{{ resource.ip }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="resource-traffic" v-if="showMetrics">
|
||||
<div
|
||||
v-if="showMetrics"
|
||||
class="resource-traffic"
|
||||
>
|
||||
<div class="traffic-in">
|
||||
<span class="traffic-label">↓</span>
|
||||
<span class="traffic-value">{{ resource.inTraffic }}</span>
|
||||
@@ -121,18 +203,20 @@
|
||||
|
||||
<!-- 图例说明 -->
|
||||
<div class="network-legend">
|
||||
<div class="legend-title">流量类型说明:</div>
|
||||
<div class="legend-title">
|
||||
流量类型说明:
|
||||
</div>
|
||||
<div class="legend-items">
|
||||
<div class="legend-item">
|
||||
<span class="legend-color inbound"></span>
|
||||
<span class="legend-color inbound" />
|
||||
<span>入向流量:用户 → 服务器</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<span class="legend-color outbound"></span>
|
||||
<span class="legend-color outbound" />
|
||||
<span>出向流量:服务器 → 外部</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<span class="legend-color east-west"></span>
|
||||
<span class="legend-color east-west" />
|
||||
<span>东西向流量:服务间通信</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+122
-45
@@ -1,60 +1,109 @@
|
||||
<template>
|
||||
<div class="resource-topology-demo">
|
||||
<div class="controls">
|
||||
<el-radio-group v-model="viewMode" size="small">
|
||||
<el-radio-button label="overview">全景视图</el-radio-button>
|
||||
<el-radio-button label="compute">计算视角</el-radio-button>
|
||||
<el-radio-button label="network">网络视角</el-radio-button>
|
||||
<el-radio-button label="storage">存储视角</el-radio-button>
|
||||
<el-radio-group
|
||||
v-model="viewMode"
|
||||
size="small"
|
||||
>
|
||||
<el-radio-button label="overview">
|
||||
全景视图
|
||||
</el-radio-button>
|
||||
<el-radio-button label="compute">
|
||||
计算视角
|
||||
</el-radio-button>
|
||||
<el-radio-button label="network">
|
||||
网络视角
|
||||
</el-radio-button>
|
||||
<el-radio-button label="storage">
|
||||
存储视角
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
|
||||
<div class="topology-container" ref="topologyRef">
|
||||
<div
|
||||
ref="topologyRef"
|
||||
class="topology-container"
|
||||
>
|
||||
<!-- 云服务商层 -->
|
||||
<div class="layer cloud-provider">
|
||||
<div class="layer-title">☁️ 云服务商</div>
|
||||
<div class="provider-grid">
|
||||
<div v-for="provider in providers" :key="provider.name"
|
||||
class="provider-card"
|
||||
:class="{ active: selectedProvider === provider.name }"
|
||||
@click="selectProvider(provider.name)">
|
||||
<div class="provider-icon">{{ provider.icon }}</div>
|
||||
<div class="provider-name">{{ provider.name }}</div>
|
||||
</div>
|
||||
<div class="layer-title">
|
||||
☁️ 云服务商
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 连接箭头 -->
|
||||
<div class="connection-arrow">⬇️</div>
|
||||
|
||||
<!-- 地域/可用区层 -->
|
||||
<div class="layer region-layer">
|
||||
<div class="layer-title">🌍 地域 & 可用区</div>
|
||||
<div class="region-grid">
|
||||
<div v-for="region in regions" :key="region.id"
|
||||
class="region-card"
|
||||
:class="{ active: selectedRegion === region.id }"
|
||||
@click="selectRegion(region.id)">
|
||||
<div class="region-name">{{ region.name }}</div>
|
||||
<div class="region-azs">
|
||||
<span v-for="az in region.azs" :key="az" class="az-badge">{{ az }}</span>
|
||||
<div class="provider-grid">
|
||||
<div
|
||||
v-for="provider in providers"
|
||||
:key="provider.name"
|
||||
class="provider-card"
|
||||
:class="{ active: selectedProvider === provider.name }"
|
||||
@click="selectProvider(provider.name)"
|
||||
>
|
||||
<div class="provider-icon">
|
||||
{{ provider.icon }}
|
||||
</div>
|
||||
<div class="provider-name">
|
||||
{{ provider.name }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 连接箭头 -->
|
||||
<div class="connection-arrow">⬇️</div>
|
||||
<div class="connection-arrow">
|
||||
⬇️
|
||||
</div>
|
||||
|
||||
<!-- 地域/可用区层 -->
|
||||
<div class="layer region-layer">
|
||||
<div class="layer-title">
|
||||
🌍 地域 & 可用区
|
||||
</div>
|
||||
<div class="region-grid">
|
||||
<div
|
||||
v-for="region in regions"
|
||||
:key="region.id"
|
||||
class="region-card"
|
||||
:class="{ active: selectedRegion === region.id }"
|
||||
@click="selectRegion(region.id)"
|
||||
>
|
||||
<div class="region-name">
|
||||
{{ region.name }}
|
||||
</div>
|
||||
<div class="region-azs">
|
||||
<span
|
||||
v-for="az in region.azs"
|
||||
:key="az"
|
||||
class="az-badge"
|
||||
>{{ az }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 连接箭头 -->
|
||||
<div class="connection-arrow">
|
||||
⬇️
|
||||
</div>
|
||||
|
||||
<!-- 资源拓扑层 -->
|
||||
<div class="layer resource-layer">
|
||||
<div class="layer-title">🎯 资源拓扑</div>
|
||||
<div class="layer-title">
|
||||
🎯 资源拓扑
|
||||
</div>
|
||||
<div class="resource-grid">
|
||||
<!-- 计算资源 -->
|
||||
<div class="resource-category" :class="{ highlight: viewMode === 'compute' || viewMode === 'overview' }">
|
||||
<div class="category-title">💻 计算</div>
|
||||
<div
|
||||
class="resource-category"
|
||||
:class="{ highlight: viewMode === 'compute' || viewMode === 'overview' }"
|
||||
>
|
||||
<div class="category-title">
|
||||
💻 计算
|
||||
</div>
|
||||
<div class="resource-list">
|
||||
<div v-for="item in computeResources" :key="item.name" class="resource-item">
|
||||
<div
|
||||
v-for="item in computeResources"
|
||||
:key="item.name"
|
||||
class="resource-item"
|
||||
>
|
||||
<span class="resource-icon">{{ item.icon }}</span>
|
||||
<span class="resource-name">{{ item.name }}</span>
|
||||
</div>
|
||||
@@ -62,10 +111,19 @@
|
||||
</div>
|
||||
|
||||
<!-- 网络资源 -->
|
||||
<div class="resource-category" :class="{ highlight: viewMode === 'network' || viewMode === 'overview' }">
|
||||
<div class="category-title">🌐 网络</div>
|
||||
<div
|
||||
class="resource-category"
|
||||
:class="{ highlight: viewMode === 'network' || viewMode === 'overview' }"
|
||||
>
|
||||
<div class="category-title">
|
||||
🌐 网络
|
||||
</div>
|
||||
<div class="resource-list">
|
||||
<div v-for="item in networkResources" :key="item.name" class="resource-item">
|
||||
<div
|
||||
v-for="item in networkResources"
|
||||
:key="item.name"
|
||||
class="resource-item"
|
||||
>
|
||||
<span class="resource-icon">{{ item.icon }}</span>
|
||||
<span class="resource-name">{{ item.name }}</span>
|
||||
</div>
|
||||
@@ -73,10 +131,19 @@
|
||||
</div>
|
||||
|
||||
<!-- 存储资源 -->
|
||||
<div class="resource-category" :class="{ highlight: viewMode === 'storage' || viewMode === 'overview' }">
|
||||
<div class="category-title">💾 存储</div>
|
||||
<div
|
||||
class="resource-category"
|
||||
:class="{ highlight: viewMode === 'storage' || viewMode === 'overview' }"
|
||||
>
|
||||
<div class="category-title">
|
||||
💾 存储
|
||||
</div>
|
||||
<div class="resource-list">
|
||||
<div v-for="item in storageResources" :key="item.name" class="resource-item">
|
||||
<div
|
||||
v-for="item in storageResources"
|
||||
:key="item.name"
|
||||
class="resource-item"
|
||||
>
|
||||
<span class="resource-icon">{{ item.icon }}</span>
|
||||
<span class="resource-name">{{ item.name }}</span>
|
||||
</div>
|
||||
@@ -87,16 +154,26 @@
|
||||
</div>
|
||||
|
||||
<!-- 信息面板 -->
|
||||
<div class="info-panel" v-if="showInfo">
|
||||
<div
|
||||
v-if="showInfo"
|
||||
class="info-panel"
|
||||
>
|
||||
<div class="info-header">
|
||||
<h4>💡 拓扑说明</h4>
|
||||
<el-button type="text" @click="showInfo = false">关闭</el-button>
|
||||
<el-button
|
||||
type="text"
|
||||
@click="showInfo = false"
|
||||
>
|
||||
关闭
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="info-content">
|
||||
<p><strong>当前视图:</strong>{{ viewModeText }}</p>
|
||||
<p><strong>选中云商:</strong>{{ selectedProvider || '未选择' }}</p>
|
||||
<p><strong>选中地域:</strong>{{ selectedRegion || '未选择' }}</p>
|
||||
<p class="tip">💡 提示:点击云服务商和地域可以查看不同组合的资源拓扑</p>
|
||||
<p class="tip">
|
||||
💡 提示:点击云服务商和地域可以查看不同组合的资源拓扑
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,30 +2,66 @@
|
||||
<div class="storage-topology-demo">
|
||||
<!-- 控制面板 -->
|
||||
<div class="control-panel">
|
||||
<el-radio-group v-model="viewMode" size="small">
|
||||
<el-radio-button label="overview">存储概览</el-radio-button>
|
||||
<el-radio-button label="object">对象存储</el-radio-button>
|
||||
<el-radio-button label="block">块存储</el-radio-button>
|
||||
<el-radio-button label="file">文件存储</el-radio-button>
|
||||
<el-radio-group
|
||||
v-model="viewMode"
|
||||
size="small"
|
||||
>
|
||||
<el-radio-button label="overview">
|
||||
存储概览
|
||||
</el-radio-button>
|
||||
<el-radio-button label="object">
|
||||
对象存储
|
||||
</el-radio-button>
|
||||
<el-radio-button label="block">
|
||||
块存储
|
||||
</el-radio-button>
|
||||
<el-radio-button label="file">
|
||||
文件存储
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
|
||||
<el-switch v-model="showDetails" active-text="显示详情" style="margin-left: 20px" />
|
||||
<el-switch
|
||||
v-model="showDetails"
|
||||
active-text="显示详情"
|
||||
style="margin-left: 20px"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 存储架构图 -->
|
||||
<div class="storage-architecture">
|
||||
<!-- 应用接入层 -->
|
||||
<div class="layer access-layer">
|
||||
<div class="layer-title">🔌 应用接入层</div>
|
||||
<div class="layer-title">
|
||||
🔌 应用接入层
|
||||
</div>
|
||||
<div class="access-methods">
|
||||
<div class="method-card" v-for="method in accessMethods" :key="method.name"
|
||||
@mouseenter="hoverMethod = method.name" @mouseleave="hoverMethod = null">
|
||||
<div class="method-icon">{{ method.icon }}</div>
|
||||
<div class="method-name">{{ method.name }}</div>
|
||||
<div class="method-desc">{{ method.description }}</div>
|
||||
<div
|
||||
v-for="method in accessMethods"
|
||||
:key="method.name"
|
||||
class="method-card"
|
||||
@mouseenter="hoverMethod = method.name"
|
||||
@mouseleave="hoverMethod = null"
|
||||
>
|
||||
<div class="method-icon">
|
||||
{{ method.icon }}
|
||||
</div>
|
||||
<div class="method-name">
|
||||
{{ method.name }}
|
||||
</div>
|
||||
<div class="method-desc">
|
||||
{{ method.description }}
|
||||
</div>
|
||||
|
||||
<div class="method-tooltip" v-if="hoverMethod === method.name && showDetails">
|
||||
<div v-for="detail in method.details" :key="detail">{{ detail }}</div>
|
||||
<div
|
||||
v-if="hoverMethod === method.name && showDetails"
|
||||
class="method-tooltip"
|
||||
>
|
||||
<div
|
||||
v-for="detail in method.details"
|
||||
:key="detail"
|
||||
>
|
||||
{{ detail }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -33,16 +69,25 @@
|
||||
|
||||
<!-- 存储网关层 -->
|
||||
<div class="layer gateway-layer">
|
||||
<div class="layer-title">🚪 存储网关层</div>
|
||||
<div class="layer-title">
|
||||
🚪 存储网关层
|
||||
</div>
|
||||
<div class="gateways-grid">
|
||||
<div class="gateway-card" v-for="gateway in storageGateways" :key="gateway.name"
|
||||
:class="gateway.type">
|
||||
<div
|
||||
v-for="gateway in storageGateways"
|
||||
:key="gateway.name"
|
||||
class="gateway-card"
|
||||
:class="gateway.type"
|
||||
>
|
||||
<div class="gateway-header">
|
||||
<span class="gateway-icon">{{ gateway.icon }}</span>
|
||||
<span class="gateway-name">{{ gateway.name }}</span>
|
||||
</div>
|
||||
|
||||
<div class="gateway-metrics" v-if="showDetails">
|
||||
<div
|
||||
v-if="showDetails"
|
||||
class="gateway-metrics"
|
||||
>
|
||||
<div class="metric">
|
||||
<span class="metric-label">TPS:</span>
|
||||
<span class="metric-value">{{ gateway.tps }}</span>
|
||||
@@ -58,17 +103,27 @@
|
||||
|
||||
<!-- 存储服务层 -->
|
||||
<div class="layer storage-services-layer">
|
||||
<div class="layer-title">💾 存储服务层</div>
|
||||
<div class="layer-title">
|
||||
💾 存储服务层
|
||||
</div>
|
||||
<div class="storage-types-grid">
|
||||
<!-- 对象存储 -->
|
||||
<div class="storage-type-card object-storage"
|
||||
:class="{ active: viewMode === 'object' || viewMode === 'overview' }">
|
||||
<div
|
||||
class="storage-type-card object-storage"
|
||||
:class="{ active: viewMode === 'object' || viewMode === 'overview' }"
|
||||
>
|
||||
<div class="storage-header">
|
||||
<div class="storage-icon">🪣</div>
|
||||
<div class="storage-title">对象存储 OSS</div>
|
||||
<div class="storage-icon">
|
||||
🪣
|
||||
</div>
|
||||
<div class="storage-title">
|
||||
对象存储 OSS
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="storage-desc">适合存储图片、视频、日志等非结构化数据</div>
|
||||
<div class="storage-desc">
|
||||
适合存储图片、视频、日志等非结构化数据
|
||||
</div>
|
||||
|
||||
<div class="storage-features">
|
||||
<div class="feature">
|
||||
@@ -85,27 +140,44 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="storage-buckets" v-if="showDetails">
|
||||
<div class="bucket" v-for="bucket in buckets" :key="bucket.name">
|
||||
<div
|
||||
v-if="showDetails"
|
||||
class="storage-buckets"
|
||||
>
|
||||
<div
|
||||
v-for="bucket in buckets"
|
||||
:key="bucket.name"
|
||||
class="bucket"
|
||||
>
|
||||
<div class="bucket-info">
|
||||
<span class="bucket-name">{{ bucket.name }}</span>
|
||||
<span class="bucket-objects">{{ bucket.objects }} 个对象</span>
|
||||
</div>
|
||||
|
||||
<div class="bucket-size">{{ bucket.size }}</div>
|
||||
<div class="bucket-size">
|
||||
{{ bucket.size }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 块存储 -->
|
||||
<div class="storage-type-card block-storage"
|
||||
:class="{ active: viewMode === 'block' || viewMode === 'overview' }">
|
||||
<div
|
||||
class="storage-type-card block-storage"
|
||||
:class="{ active: viewMode === 'block' || viewMode === 'overview' }"
|
||||
>
|
||||
<div class="storage-header">
|
||||
<div class="storage-icon">💽</div>
|
||||
<div class="storage-title">块存储 EBS</div>
|
||||
<div class="storage-icon">
|
||||
💽
|
||||
</div>
|
||||
<div class="storage-title">
|
||||
块存储 EBS
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="storage-desc">为云服务器提供高性能、低延迟的数据块存储</div>
|
||||
<div class="storage-desc">
|
||||
为云服务器提供高性能、低延迟的数据块存储
|
||||
</div>
|
||||
|
||||
<div class="storage-features">
|
||||
<div class="feature">
|
||||
@@ -122,12 +194,22 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="volumes-list" v-if="showDetails">
|
||||
<div class="volume" v-for="vol in volumes" :key="vol.id">
|
||||
<div
|
||||
v-if="showDetails"
|
||||
class="volumes-list"
|
||||
>
|
||||
<div
|
||||
v-for="vol in volumes"
|
||||
:key="vol.id"
|
||||
class="volume"
|
||||
>
|
||||
<div class="volume-info">
|
||||
<div class="volume-header">
|
||||
<span class="volume-name">{{ vol.name }}</span>
|
||||
<span class="volume-type" :class="vol.type">{{ vol.type }}</span>
|
||||
<span
|
||||
class="volume-type"
|
||||
:class="vol.type"
|
||||
>{{ vol.type }}</span>
|
||||
</div>
|
||||
|
||||
<div class="volume-meta">
|
||||
@@ -137,23 +219,38 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="volume-iops" v-if="vol.iops">
|
||||
<div class="iops-label">IOPS</div>
|
||||
<div class="iops-value">{{ vol.iops }}</div>
|
||||
<div
|
||||
v-if="vol.iops"
|
||||
class="volume-iops"
|
||||
>
|
||||
<div class="iops-label">
|
||||
IOPS
|
||||
</div>
|
||||
<div class="iops-value">
|
||||
{{ vol.iops }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 文件存储 -->
|
||||
<div class="storage-type-card file-storage"
|
||||
:class="{ active: viewMode === 'file' || viewMode === 'overview' }">
|
||||
<div
|
||||
class="storage-type-card file-storage"
|
||||
:class="{ active: viewMode === 'file' || viewMode === 'overview' }"
|
||||
>
|
||||
<div class="storage-header">
|
||||
<div class="storage-icon">📁</div>
|
||||
<div class="storage-title">文件存储 NAS</div>
|
||||
<div class="storage-icon">
|
||||
📁
|
||||
</div>
|
||||
<div class="storage-title">
|
||||
文件存储 NAS
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="storage-desc">为多个计算节点提供共享文件系统访问</div>
|
||||
<div class="storage-desc">
|
||||
为多个计算节点提供共享文件系统访问
|
||||
</div>
|
||||
|
||||
<div class="storage-features">
|
||||
<div class="feature">
|
||||
@@ -170,12 +267,22 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="filesystems-list" v-if="showDetails">
|
||||
<div class="filesystem" v-for="fs in filesystems" :key="fs.name">
|
||||
<div
|
||||
v-if="showDetails"
|
||||
class="filesystems-list"
|
||||
>
|
||||
<div
|
||||
v-for="fs in filesystems"
|
||||
:key="fs.name"
|
||||
class="filesystem"
|
||||
>
|
||||
<div class="fs-header">
|
||||
<div class="fs-info">
|
||||
<span class="fs-name">{{ fs.name }}</span>
|
||||
<span class="fs-protocol" :class="fs.protocol">{{ fs.protocol }}</span>
|
||||
<span
|
||||
class="fs-protocol"
|
||||
:class="fs.protocol"
|
||||
>{{ fs.protocol }}</span>
|
||||
</div>
|
||||
|
||||
<div class="fs-capacity">
|
||||
@@ -185,14 +292,23 @@
|
||||
</div>
|
||||
|
||||
<div class="fs-capacity-bar">
|
||||
<div class="capacity-progress" :style="{ width: fs.percentage + '%' }"
|
||||
:class="{ warning: fs.percentage > 80, danger: fs.percentage > 90 }"></div>
|
||||
<div
|
||||
class="capacity-progress"
|
||||
:style="{ width: fs.percentage + '%' }"
|
||||
:class="{ warning: fs.percentage > 80, danger: fs.percentage > 90 }"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="fs-mounts">
|
||||
<div class="mounts-label">挂载点:</div>
|
||||
<div class="mounts-label">
|
||||
挂载点:
|
||||
</div>
|
||||
<div class="mounts-list">
|
||||
<span class="mount-point" v-for="mount in fs.mounts" :key="mount">{{ mount }}</span>
|
||||
<span
|
||||
v-for="mount in fs.mounts"
|
||||
:key="mount"
|
||||
class="mount-point"
|
||||
>{{ mount }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -204,21 +320,52 @@
|
||||
|
||||
<!-- 存储选型对比 -->
|
||||
<div class="comparison-section">
|
||||
<div class="comparison-title">📊 存储类型选型对比</div>
|
||||
<div class="comparison-title">
|
||||
📊 存储类型选型对比
|
||||
</div>
|
||||
|
||||
<div class="comparison-table">
|
||||
<div class="table-header">
|
||||
<div class="header-cell">特性</div>
|
||||
<div class="header-cell object">对象存储</div>
|
||||
<div class="header-cell block">块存储</div>
|
||||
<div class="header-cell file">文件存储</div>
|
||||
<div class="header-cell">
|
||||
特性
|
||||
</div>
|
||||
<div class="header-cell object">
|
||||
对象存储
|
||||
</div>
|
||||
<div class="header-cell block">
|
||||
块存储
|
||||
</div>
|
||||
<div class="header-cell file">
|
||||
文件存储
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-row" v-for="row in comparisonData" :key="row.feature">
|
||||
<div class="cell feature">{{ row.feature }}</div>
|
||||
<div class="cell" :class="{ highlight: row.object === '优秀' || row.object === '强' }">{{ row.object }}</div>
|
||||
<div class="cell" :class="{ highlight: row.block === '优秀' || row.block === '强' }">{{ row.block }}</div>
|
||||
<div class="cell" :class="{ highlight: row.file === '优秀' || row.file === '强' }">{{ row.file }}</div>
|
||||
<div
|
||||
v-for="row in comparisonData"
|
||||
:key="row.feature"
|
||||
class="table-row"
|
||||
>
|
||||
<div class="cell feature">
|
||||
{{ row.feature }}
|
||||
</div>
|
||||
<div
|
||||
class="cell"
|
||||
:class="{ highlight: row.object === '优秀' || row.object === '强' }"
|
||||
>
|
||||
{{ row.object }}
|
||||
</div>
|
||||
<div
|
||||
class="cell"
|
||||
:class="{ highlight: row.block === '优秀' || row.block === '强' }"
|
||||
>
|
||||
{{ row.block }}
|
||||
</div>
|
||||
<div
|
||||
class="cell"
|
||||
:class="{ highlight: row.file === '优秀' || row.file === '强' }"
|
||||
>
|
||||
{{ row.file }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,19 +4,39 @@
|
||||
<div class="control-panel">
|
||||
<div class="panel-section">
|
||||
<span class="panel-label">VPC 网段:</span>
|
||||
<el-radio-group v-model="vpcCidr" size="small">
|
||||
<el-radio-button label="172.16.0.0/12">172.16.0.0/12</el-radio-button>
|
||||
<el-radio-button label="10.0.0.0/8">10.0.0.0/8</el-radio-button>
|
||||
<el-radio-button label="192.168.0.0/16">192.168.0.0/16</el-radio-button>
|
||||
<el-radio-group
|
||||
v-model="vpcCidr"
|
||||
size="small"
|
||||
>
|
||||
<el-radio-button label="172.16.0.0/12">
|
||||
172.16.0.0/12
|
||||
</el-radio-button>
|
||||
<el-radio-button label="10.0.0.0/8">
|
||||
10.0.0.0/8
|
||||
</el-radio-button>
|
||||
<el-radio-button label="192.168.0.0/16">
|
||||
192.168.0.0/16
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
|
||||
<div class="panel-section">
|
||||
<span class="panel-label">子网划分:</span>
|
||||
<el-slider v-model="subnetBits" :min="2" :max="4" show-stops :marks="{2: '/24', 3: '/25', 4: '/26'}" style="width: 200px;" />
|
||||
<el-slider
|
||||
v-model="subnetBits"
|
||||
:min="2"
|
||||
:max="4"
|
||||
show-stops
|
||||
:marks="{2: '/24', 3: '/25', 4: '/26'}"
|
||||
style="width: 200px;"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<el-switch v-model="showCalculation" active-text="显示计算过程" style="margin-left: 20px;" />
|
||||
<el-switch
|
||||
v-model="showCalculation"
|
||||
active-text="显示计算过程"
|
||||
style="margin-left: 20px;"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 网段可视化 -->
|
||||
@@ -42,14 +62,19 @@
|
||||
<span class="cell-type">{{ subnet.type === 'public' ? '🌐' : '🔒' }}</span>
|
||||
<span class="cell-name">{{ subnet.name }}</span>
|
||||
</div>
|
||||
<div class="cell-cidr">{{ subnet.cidr }}</div>
|
||||
<div class="cell-cidr">
|
||||
{{ subnet.cidr }}
|
||||
</div>
|
||||
<div class="cell-stats">
|
||||
<span class="ip-count">{{ subnet.ipCount }} IP</span>
|
||||
<span class="az-badge">{{ subnet.az }}</span>
|
||||
</div>
|
||||
|
||||
<!-- 悬停提示 -->
|
||||
<div class="cell-tooltip" v-if="hoverSubnet === index && showCalculation">
|
||||
<div
|
||||
v-if="hoverSubnet === index && showCalculation"
|
||||
class="cell-tooltip"
|
||||
>
|
||||
<div class="tooltip-row">
|
||||
<span>网段范围:</span>
|
||||
<code>{{ subnet.range }}</code>
|
||||
@@ -69,7 +94,10 @@
|
||||
</div>
|
||||
|
||||
<!-- 网段计算说明 -->
|
||||
<div class="calculation-panel" v-if="showCalculation">
|
||||
<div
|
||||
v-if="showCalculation"
|
||||
class="calculation-panel"
|
||||
>
|
||||
<h4>📐 子网划分计算说明</h4>
|
||||
|
||||
<div class="calc-section">
|
||||
@@ -118,21 +146,27 @@
|
||||
<h4>💡 子网设计最佳实践</h4>
|
||||
<div class="tips-grid">
|
||||
<div class="tip-item">
|
||||
<div class="tip-icon">🎯</div>
|
||||
<div class="tip-icon">
|
||||
🎯
|
||||
</div>
|
||||
<div class="tip-content">
|
||||
<h5>预留足够 IP</h5>
|
||||
<p>每个子网至少预留 20% 的 IP 作为扩容缓冲</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tip-item">
|
||||
<div class="tip-icon">🔒</div>
|
||||
<div class="tip-icon">
|
||||
🔒
|
||||
</div>
|
||||
<div class="tip-content">
|
||||
<h5>公网私网分离</h5>
|
||||
<p>核心数据放在私网子网,通过 NAT 访问外网</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tip-item">
|
||||
<div class="tip-icon">🌐</div>
|
||||
<div class="tip-icon">
|
||||
🌐
|
||||
</div>
|
||||
<div class="tip-content">
|
||||
<h5>多 AZ 部署</h5>
|
||||
<p>同一 VPC 的不同子网放在不同可用区</p>
|
||||
|
||||
@@ -2,11 +2,22 @@
|
||||
<div class="vpc-architecture-demo">
|
||||
<!-- 控制面板 -->
|
||||
<div class="control-panel">
|
||||
<el-radio-group v-model="viewMode" size="small">
|
||||
<el-radio-button label="full">完整架构</el-radio-button>
|
||||
<el-radio-button label="public">公网访问</el-radio-button>
|
||||
<el-radio-button label="private">私网隔离</el-radio-button>
|
||||
<el-radio-button label="hybrid">混合云</el-radio-button>
|
||||
<el-radio-group
|
||||
v-model="viewMode"
|
||||
size="small"
|
||||
>
|
||||
<el-radio-button label="full">
|
||||
完整架构
|
||||
</el-radio-button>
|
||||
<el-radio-button label="public">
|
||||
公网访问
|
||||
</el-radio-button>
|
||||
<el-radio-button label="private">
|
||||
私网隔离
|
||||
</el-radio-button>
|
||||
<el-radio-button label="hybrid">
|
||||
混合云
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
<el-switch
|
||||
v-model="showDetails"
|
||||
@@ -18,126 +29,183 @@
|
||||
<!-- VPC 架构图 -->
|
||||
<div class="vpc-container">
|
||||
<!-- 外部互联网 -->
|
||||
<div class="internet-zone" v-if="showInternet">
|
||||
<div
|
||||
v-if="showInternet"
|
||||
class="internet-zone"
|
||||
>
|
||||
<div class="zone-header">
|
||||
<span class="zone-icon">🌐</span>
|
||||
<span class="zone-title">互联网 (Internet)</span>
|
||||
</div>
|
||||
<div class="zone-content">
|
||||
<div class="internet-user">
|
||||
<div class="user-avatar">👤</div>
|
||||
<div class="user-label">用户</div>
|
||||
<div class="user-avatar">
|
||||
👤
|
||||
</div>
|
||||
<div class="user-label">
|
||||
用户
|
||||
</div>
|
||||
</div>
|
||||
<div class="internet-user">
|
||||
<div class="user-avatar">🏢</div>
|
||||
<div class="user-label">企业</div>
|
||||
<div class="user-avatar">
|
||||
🏢
|
||||
</div>
|
||||
<div class="user-label">
|
||||
企业
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 连接箭头 -->
|
||||
<div class="connection-flow" v-if="showInternet">
|
||||
<div class="flow-line"></div>
|
||||
<div
|
||||
v-if="showInternet"
|
||||
class="connection-flow"
|
||||
>
|
||||
<div class="flow-line" />
|
||||
<div class="flow-devices">
|
||||
<div class="device" v-for="device in borderDevices" :key="device.name"
|
||||
:class="device.type"
|
||||
@mouseenter="hoverDevice = device.name"
|
||||
@mouseleave="hoverDevice = null"
|
||||
003e
|
||||
<div class="device-icon">{{ device.icon }}</div>
|
||||
<div class="device-name">{{ device.name }}</div>
|
||||
<div class="device-tooltip" v-if="hoverDevice === device.name && showDetails">
|
||||
{{ device.description }}
|
||||
</div>
|
||||
<div
|
||||
v-for="device in borderDevices"
|
||||
:key="device.name"
|
||||
class="device"
|
||||
:class="device.type"
|
||||
003e
|
||||
<div
|
||||
class="device-icon"
|
||||
@mouseenter="hoverDevice = device.name"
|
||||
@mouseleave="hoverDevice = null"
|
||||
>
|
||||
{{ device.icon }}
|
||||
</div>
|
||||
<div class="device-name">
|
||||
{{ device.name }}
|
||||
</div>
|
||||
<div
|
||||
v-if="hoverDevice === device.name && showDetails"
|
||||
class="device-tooltip"
|
||||
>
|
||||
{{ device.description }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- VPC 主体 -->
|
||||
<div class="vpc-zone">
|
||||
<div class="vpc-header">
|
||||
<div class="vpc-title">
|
||||
<span class="vpc-icon">🏠</span>
|
||||
<span>专有网络 VPC</span>
|
||||
<span class="vpc-id">vpc-2ze7p8w7c9d6x5y4</span>
|
||||
<!-- VPC 主体 -->
|
||||
<div class="vpc-zone">
|
||||
<div class="vpc-header">
|
||||
<div class="vpc-title">
|
||||
<span class="vpc-icon">🏠</span>
|
||||
<span>专有网络 VPC</span>
|
||||
<span class="vpc-id">vpc-2ze7p8w7c9d6x5y4</span>
|
||||
</div>
|
||||
<div class="vpc-meta">
|
||||
<span class="meta-item">📍 华北2 (北京)</span>
|
||||
<span class="meta-item">🌐 172.16.0.0/12</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="vpc-content">
|
||||
<!-- 可用区 1 -->
|
||||
<div class="az-container">
|
||||
<div class="az-header">
|
||||
<span class="az-name">可用区 A</span>
|
||||
<span class="az-status online">在线</span>
|
||||
</div>
|
||||
<div class="vpc-meta">
|
||||
<span class="meta-item">📍 华北2 (北京)</span>
|
||||
<span class="meta-item">🌐 172.16.0.0/12</span>
|
||||
<div class="subnets">
|
||||
<div
|
||||
class="subnet public"
|
||||
@mouseenter="hoverSubnet = 'public-a'"
|
||||
@mouseleave="hoverSubnet = null"
|
||||
>
|
||||
<div class="subnet-header">
|
||||
<span class="subnet-type">🌐 公网子网</span>
|
||||
<span class="subnet-cidr">172.16.1.0/24</span>
|
||||
</div>
|
||||
<div class="subnet-resources">
|
||||
<div class="resource-tag">
|
||||
🖥️ ECS × 2
|
||||
</div>
|
||||
<div class="resource-tag">
|
||||
⚖️ SLB
|
||||
</div>
|
||||
<div class="resource-tag">
|
||||
🌐 NAT
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="hoverSubnet === 'public-a' && showDetails"
|
||||
class="subnet-tooltip"
|
||||
>
|
||||
公网子网:可直接访问互联网,部署对外服务
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="subnet private"
|
||||
@mouseenter="hoverSubnet = 'private-a'"
|
||||
@mouseleave="hoverSubnet = null"
|
||||
>
|
||||
<div class="subnet-header">
|
||||
<span class="subnet-type">🔒 私网子网</span>
|
||||
<span class="subnet-cidr">172.16.2.0/24</span>
|
||||
</div>
|
||||
<div class="subnet-resources">
|
||||
<div class="resource-tag">
|
||||
🖥️ ECS × 4
|
||||
</div>
|
||||
<div class="resource-tag">
|
||||
🗄️ RDS
|
||||
</div>
|
||||
<div class="resource-tag">
|
||||
📦 Redis
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="hoverSubnet === 'private-a' && showDetails"
|
||||
class="subnet-tooltip"
|
||||
>
|
||||
私网子网:无法直接访问互联网,部署核心服务
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="vpc-content">
|
||||
<!-- 可用区 1 -->
|
||||
<div class="az-container">
|
||||
<div class="az-header">
|
||||
<span class="az-name">可用区 A</span>
|
||||
<span class="az-status online">在线</span>
|
||||
</div>
|
||||
<div class="subnets">
|
||||
<div class="subnet public"
|
||||
@mouseenter="hoverSubnet = 'public-a'"
|
||||
@mouseleave="hoverSubnet = null">
|
||||
<div class="subnet-header">
|
||||
<span class="subnet-type">🌐 公网子网</span>
|
||||
<span class="subnet-cidr">172.16.1.0/24</span>
|
||||
</div>
|
||||
<div class="subnet-resources">
|
||||
<div class="resource-tag">🖥️ ECS × 2</div>
|
||||
<div class="resource-tag">⚖️ SLB</div>
|
||||
<div class="resource-tag">🌐 NAT</div>
|
||||
</div>
|
||||
<div class="subnet-tooltip" v-if="hoverSubnet === 'public-a' && showDetails">
|
||||
公网子网:可直接访问互联网,部署对外服务
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="subnet private"
|
||||
@mouseenter="hoverSubnet = 'private-a'"
|
||||
@mouseleave="hoverSubnet = null">
|
||||
<div class="subnet-header">
|
||||
<span class="subnet-type">🔒 私网子网</span>
|
||||
<span class="subnet-cidr">172.16.2.0/24</span>
|
||||
</div>
|
||||
<div class="subnet-resources">
|
||||
<div class="resource-tag">🖥️ ECS × 4</div>
|
||||
<div class="resource-tag">🗄️ RDS</div>
|
||||
<div class="resource-tag">📦 Redis</div>
|
||||
</div>
|
||||
<div class="subnet-tooltip" v-if="hoverSubnet === 'private-a' && showDetails">
|
||||
私网子网:无法直接访问互联网,部署核心服务
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 可用区 2 -->
|
||||
<div class="az-container">
|
||||
<div class="az-header">
|
||||
<span class="az-name">可用区 B</span>
|
||||
<span class="az-status online">在线</span>
|
||||
</div>
|
||||
|
||||
<!-- 可用区 2 -->
|
||||
<div class="az-container">
|
||||
<div class="az-header">
|
||||
<span class="az-name">可用区 B</span>
|
||||
<span class="az-status online">在线</span>
|
||||
</div>
|
||||
<div class="subnets">
|
||||
<div class="subnet public">
|
||||
<div class="subnet-header">
|
||||
<span class="subnet-type">🌐 公网子网</span>
|
||||
<span class="subnet-cidr">172.16.3.0/24</span>
|
||||
<div class="subnets">
|
||||
<div class="subnet public">
|
||||
<div class="subnet-header">
|
||||
<span class="subnet-type">🌐 公网子网</span>
|
||||
<span class="subnet-cidr">172.16.3.0/24</span>
|
||||
</div>
|
||||
<div class="subnet-resources">
|
||||
<div class="resource-tag">
|
||||
🖥️ ECS × 2
|
||||
</div>
|
||||
<div class="subnet-resources">
|
||||
<div class="resource-tag">🖥️ ECS × 2</div>
|
||||
<div class="resource-tag">⚖️ SLB</div>
|
||||
<div class="resource-tag">
|
||||
⚖️ SLB
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="subnet private">
|
||||
<div class="subnet-header">
|
||||
<span class="subnet-type">🔒 私网子网</span>
|
||||
<span class="subnet-cidr">172.16.4.0/24</span>
|
||||
<div class="subnet private">
|
||||
<div class="subnet-header">
|
||||
<span class="subnet-type">🔒 私网子网</span>
|
||||
<span class="subnet-cidr">172.16.4.0/24</span>
|
||||
</div>
|
||||
<div class="subnet-resources">
|
||||
<div class="resource-tag">
|
||||
🖥️ ECS × 4
|
||||
</div>
|
||||
<div class="subnet-resources">
|
||||
<div class="resource-tag">🖥️ ECS × 4</div>
|
||||
<div class="resource-tag">🗄️ RDS Slave</div>
|
||||
<div class="resource-tag">📦 Redis Slave</div>
|
||||
<div class="resource-tag">
|
||||
🗄️ RDS Slave
|
||||
</div>
|
||||
<div class="resource-tag">
|
||||
📦 Redis Slave
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -146,6 +214,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
Reference in New Issue
Block a user