Files
test-repo/docs/.vitepress/theme/components/appendix/cloud-topology/StorageTopologyDemo.vue
T

1023 lines
22 KiB
Vue
Raw Normal View History

<template>
<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>
<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="access-methods">
<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
v-if="hoverMethod === method.name && showDetails"
class="method-tooltip"
>
<div
v-for="detail in method.details"
:key="detail"
>
{{ detail }}
</div>
</div>
</div>
</div>
</div>
<!-- 存储网关层 -->
<div class="layer gateway-layer">
<div class="layer-title">
🚪 存储网关层
</div>
<div class="gateways-grid">
<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
v-if="showDetails"
class="gateway-metrics"
>
<div class="metric">
<span class="metric-label">TPS</span>
<span class="metric-value">{{ gateway.tps }}</span>
</div>
<div class="metric">
<span class="metric-label">延迟</span>
<span class="metric-value">{{ gateway.latency }}ms</span>
</div>
</div>
</div>
</div>
</div>
<!-- 存储服务层 -->
<div class="layer storage-services-layer">
<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-header">
<div class="storage-icon">
🪣
</div>
<div class="storage-title">
对象存储 OSS
</div>
</div>
<div class="storage-desc">
适合存储图片视频日志等非结构化数据
</div>
<div class="storage-features">
<div class="feature">
<span class="feature-icon"></span>
<span>海量存储</span>
</div>
<div class="feature">
<span class="feature-icon"></span>
<span>低成本</span>
</div>
<div class="feature">
<span class="feature-icon"></span>
<span>CDN 加速</span>
</div>
</div>
<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>
</div>
</div>
<!-- 块存储 -->
<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>
<div class="storage-desc">
为云服务器提供高性能低延迟的数据块存储
</div>
<div class="storage-features">
<div class="feature">
<span class="feature-icon"></span>
<span>高性能 SSD</span>
</div>
<div class="feature">
<span class="feature-icon"></span>
<span>快照备份</span>
</div>
<div class="feature">
<span class="feature-icon"></span>
<span>弹性扩容</span>
</div>
</div>
<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>
</div>
<div class="volume-meta">
<span>{{ vol.size }}</span>
<span></span>
<span>挂载到: {{ vol.attachedTo }}</span>
</div>
</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-header">
<div class="storage-icon">
📁
</div>
<div class="storage-title">
文件存储 NAS
</div>
</div>
<div class="storage-desc">
为多个计算节点提供共享文件系统访问
</div>
<div class="storage-features">
<div class="feature">
<span class="feature-icon"></span>
<span>共享访问</span>
</div>
<div class="feature">
<span class="feature-icon"></span>
<span>POSIX 兼容</span>
</div>
<div class="feature">
<span class="feature-icon"></span>
<span>自动扩容</span>
</div>
</div>
<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>
</div>
<div class="fs-capacity">
<span class="capacity-used">{{ fs.used }}</span>
<span class="capacity-total">/ {{ fs.total }}</span>
</div>
</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="fs-mounts">
<div class="mounts-label">
挂载点
</div>
<div class="mounts-list">
<span
v-for="mount in fs.mounts"
:key="mount"
class="mount-point"
>{{ mount }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 存储选型对比 -->
<div class="comparison-section">
<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>
<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>
</div>
</template>
<script setup>
import { ref } from 'vue'
const viewMode = ref('overview')
const showDetails = ref(false)
const hoverMethod = ref(null)
// 接入方式
const accessMethods = [
{
name: 'API/SDK',
icon: '🔧',
description: '通过编程接口访问存储',
details: ['支持 RESTful API', '提供多语言 SDK', '支持批量操作', '可编程访问控制']
},
{
name: '挂载访问',
icon: '🔗',
description: '像本地磁盘一样使用',
details: ['支持 NFS 协议', '支持 SMB 协议', 'POSIX 兼容', '透明访问']
},
{
name: '控制台',
icon: '🖥️',
description: '通过 Web 界面管理',
details: ['可视化操作', '权限管理', '监控报表', '日志审计']
}
]
// 存储网关
const storageGateways = [
{ name: '对象网关', icon: '🪣', type: 'object', tps: '10000', latency: '5' },
{ name: '块网关', icon: '💽', type: 'block', tps: '50000', latency: '1' },
{ name: '文件网关', icon: '📁', type: 'file', tps: '8000', latency: '3' }
]
// 存储桶
const buckets = [
{ name: 'images-bucket', protocol: 'S3', used: '2.5 TB', total: '10 TB', percentage: 25, mounts: ['CDN 加速', '图片处理'] },
{ name: 'logs-bucket', protocol: 'S3', used: '850 GB', total: '5 TB', percentage: 17, mounts: ['日志归档', '数据分析'] },
{ name: 'backup-bucket', protocol: 'S3', used: '4.2 TB', total: '5 TB', percentage: 84, mounts: ['自动备份', '跨区域复制'] }
]
// 云盘
const volumes = [
{ name: 'data-disk-01', type: 'ESSD', size: '500 GB', used: '320 GB', percentage: 64, attachedTo: 'DB-Server-01', iops: 50000 },
{ name: 'data-disk-02', type: 'SSD', size: '200 GB', used: '145 GB', percentage: 72, attachedTo: 'App-Server-02', iops: 25000 },
{ name: 'log-disk-01', type: 'SATA', size: '1 TB', used: '680 GB', percentage: 68, attachedTo: 'Log-Server-01', iops: 5000 }
]
// 文件系统
const filesystems = [
{ name: 'shared-data', protocol: 'NFS', used: '1.2 TB', total: '5 TB', percentage: 24, mounts: ['/mnt/shared'] },
{ name: 'dev-env', protocol: 'NFS', used: '450 GB', total: '2 TB', percentage: 22, mounts: ['/mnt/dev'] },
{ name: 'team-share', protocol: 'SMB', used: '890 GB', total: '3 TB', percentage: 30, mounts: ['\\\\nas\\team'] }
]
// 对比数据
const comparisonData = [
{ feature: '访问协议', object: 'HTTP/HTTPS', block: 'iSCSI/NVMe', file: 'NFS/SMB' },
{ feature: '性能', object: '高吞吐', block: '低延迟', file: '中等' },
{ feature: '数据共享', object: '弱', block: '不支持', file: '强' },
{ feature: '容量扩展', object: '强', block: '中等', file: '中等' },
{ feature: '成本', object: '低', block: '高', file: '中等' },
{ feature: '典型场景', object: '图片/视频/备份', block: '数据库/应用', file: '共享文件/开发' }
]
</script>
<style scoped>
.storage-topology-demo {
padding: 20px;
background: #f5f7fa;
border-radius: 6px;
}
.control-panel {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding: 16px;
background: white;
border-radius: 6px;
flex-wrap: wrap;
gap: 12px;
}
.storage-architecture {
display: flex;
flex-direction: column;
gap: 16px;
margin-bottom: 20px;
}
.layer {
background: white;
border-radius: 12px;
padding: 16px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}
.layer-title {
font-size: 14px;
font-weight: 600;
color: #606266;
margin-bottom: 12px;
text-transform: uppercase;
letter-spacing: 1px;
}
/* Access Layer */
.access-methods {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 12px;
}
.method-card {
background: #f5f7fa;
border-radius: 6px;
padding: 16px;
text-align: center;
position: relative;
cursor: pointer;
transition: all 0.3s;
}
.method-card:hover {
background: #ecf5ff;
transform: translateY(-2px);
}
.method-icon {
font-size: 32px;
margin-bottom: 8px;
}
.method-name {
font-size: 14px;
font-weight: 600;
color: #303133;
margin-bottom: 4px;
}
.method-desc {
font-size: 12px;
color: #909399;
}
.method-tooltip {
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%);
background: #333;
color: white;
padding: 10px 14px;
border-radius: 6px;
font-size: 12px;
z-index: 10;
margin-bottom: 8px;
white-space: nowrap;
}
/* Gateway Layer */
.gateways-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 12px;
}
.gateway-card {
background: #f5f7fa;
border-radius: 6px;
padding: 12px;
border-left: 4px solid;
}
.gateway-card.object {
border-left-color: #409eff;
}
.gateway-card.block {
border-left-color: #67c23a;
}
.gateway-card.file {
border-left-color: #e6a23c;
}
.gateway-header {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 10px;
}
.gateway-icon {
font-size: 20px;
}
.gateway-name {
font-size: 14px;
font-weight: 600;
color: #303133;
}
.gateway-metrics {
display: flex;
flex-direction: column;
gap: 4px;
}
.metric {
display: flex;
justify-content: space-between;
font-size: 12px;
}
.metric-label {
color: #909399;
}
.metric-value {
color: #409eff;
font-weight: 500;
}
/* Storage Types Grid */
.storage-types-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: 16px;
}
.storage-type-card {
background: #f5f7fa;
border-radius: 12px;
padding: 16px;
border: 2px solid transparent;
transition: all 0.3s;
}
.storage-type-card:hover,
.storage-type-card.active {
border-color: #409eff;
background: #ecf5ff;
}
.storage-header {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 8px;
}
.storage-icon {
font-size: 28px;
}
.storage-title {
font-size: 16px;
font-weight: 600;
color: #303133;
}
.storage-desc {
font-size: 12px;
color: #606266;
margin-bottom: 12px;
line-height: 1.5;
}
.storage-features {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 12px;
}
.feature {
display: flex;
align-items: center;
gap: 4px;
font-size: 12px;
color: #606266;
}
.feature-icon {
color: #67c23a;
}
/* Buckets List */
.storage-buckets {
display: flex;
flex-direction: column;
gap: 8px;
margin-top: 12px;
padding-top: 12px;
border-top: 1px solid #e4e7ed;
}
.bucket {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 10px;
background: white;
border-radius: 6px;
}
.bucket-info {
display: flex;
flex-direction: column;
gap: 2px;
}
.bucket-name {
font-size: 13px;
font-weight: 500;
color: #303133;
}
.bucket-objects {
font-size: 11px;
color: #909399;
}
.bucket-size {
font-size: 12px;
color: #409eff;
font-weight: 500;
}
/* Volumes List */
.volumes-list {
display: flex;
flex-direction: column;
gap: 8px;
margin-top: 12px;
padding-top: 12px;
border-top: 1px solid #e4e7ed;
}
.volume {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
background: white;
border-radius: 6px;
}
.volume-info {
flex: 1;
}
.volume-header {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 4px;
}
.volume-name {
font-size: 13px;
font-weight: 500;
color: #303133;
}
.volume-type {
font-size: 10px;
padding: 1px 6px;
border-radius: 10px;
text-transform: uppercase;
}
.volume-type.essd {
background: #409eff;
color: white;
}
.volume-type.ssd {
background: #67c23a;
color: white;
}
.volume-type.sata {
background: #909399;
color: white;
}
.volume-meta {
font-size: 11px;
color: #909399;
}
.volume-meta span {
margin: 0 4px;
}
.volume-iops {
text-align: center;
padding-left: 12px;
border-left: 1px solid #e4e7ed;
}
.iops-label {
font-size: 10px;
color: #909399;
text-transform: uppercase;
}
.iops-value {
font-size: 14px;
font-weight: 600;
color: #409eff;
}
/* Filesystems List */
.filesystems-list {
display: flex;
flex-direction: column;
gap: 8px;
margin-top: 12px;
padding-top: 12px;
border-top: 1px solid #e4e7ed;
}
.filesystem {
background: white;
border-radius: 6px;
padding: 12px;
}
.fs-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}
.fs-info {
display: flex;
align-items: center;
gap: 8px;
}
.fs-name {
font-size: 14px;
font-weight: 500;
color: #303133;
}
.fs-protocol {
font-size: 10px;
padding: 2px 8px;
border-radius: 10px;
font-weight: 500;
}
.fs-protocol.nfs {
background: #409eff;
color: white;
}
.fs-protocol.smb {
background: #67c23a;
color: white;
}
.fs-capacity {
display: flex;
align-items: center;
gap: 4px;
font-size: 13px;
}
.capacity-used {
color: #303133;
font-weight: 500;
}
.capacity-total {
color: #909399;
}
.fs-capacity-bar {
height: 4px;
background: #e4e7ed;
border-radius: 2px;
margin-bottom: 8px;
overflow: hidden;
}
.capacity-progress {
height: 100%;
background: #67c23a;
border-radius: 2px;
transition: width 0.3s;
}
.capacity-progress.warning {
background: #e6a23c;
}
.capacity-progress.danger {
background: #f56c6c;
}
.fs-mounts {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.mounts-label {
font-size: 12px;
color: #909399;
}
.mounts-list {
display: flex;
gap: 6px;
flex-wrap: wrap;
}
.mount-point {
font-size: 11px;
padding: 2px 8px;
background: #ecf5ff;
color: #409eff;
border-radius: 10px;
}
/* Comparison Section */
.comparison-section {
margin-top: 20px;
padding: 20px;
background: white;
border-radius: 12px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}
.comparison-title {
font-size: 16px;
font-weight: 600;
color: #303133;
margin-bottom: 16px;
}
.comparison-table {
overflow-x: auto;
}
.table-header {
display: grid;
grid-template-columns: 120px repeat(3, 1fr);
gap: 1px;
background: #e4e7ed;
border-radius: 8px 8px 0 0;
overflow: hidden;
}
.header-cell {
padding: 12px;
background: #f5f7fa;
font-size: 13px;
font-weight: 600;
color: #606266;
text-align: center;
}
.header-cell.object {
background: #ecf5ff;
color: #409eff;
}
.header-cell.block {
background: #f0f9eb;
color: #67c23a;
}
.header-cell.file {
background: #fdf6ec;
color: #e6a23c;
}
.table-row {
display: grid;
grid-template-columns: 120px repeat(3, 1fr);
gap: 1px;
background: #e4e7ed;
border-bottom: 1px solid #e4e7ed;
}
.table-row:last-child {
border-radius: 0 0 8px 8px;
overflow: hidden;
border-bottom: none;
}
.cell {
padding: 10px 12px;
background: white;
font-size: 12px;
color: #606266;
text-align: center;
}
.cell.feature {
text-align: left;
font-weight: 500;
color: #303133;
background: #fafafa;
}
.cell.highlight {
font-weight: 600;
color: #67c23a;
}
@media (max-width: 768px) {
.control-panel {
flex-direction: column;
align-items: stretch;
}
.storage-types-grid {
grid-template-columns: 1fr;
}
.comparison-table {
font-size: 11px;
}
.table-header,
.table-row {
grid-template-columns: 80px repeat(3, 1fr);
}
.header-cell,
.cell {
padding: 6px 8px;
}
}
</style>