feat(docs): add interactive demo components for technical appendices

Add placeholder Vue components for visualizing technical concepts across multiple domains including frontend routing, browser rendering, cache design, queue design, database principles, API design, cloud services, and backend evolution. These components provide interactive educational content for the documentation.

Update documentation structure to include new appendix sections and enhance existing content with visual components. Remove unused 'codex' dependency from package.json.
This commit is contained in:
sanbuphy
2026-02-06 03:34:50 +08:00
parent e8bba6f7c0
commit 7c70c37072
171 changed files with 69830 additions and 6689 deletions
@@ -0,0 +1,356 @@
<template>
<div class="aws-vs-aliyun-demo">
<div class="demo-header">
<h4>AWS vs 阿里云 核心差异</h4>
<p class="demo-desc">点击切换查看不同维度的对比</p>
</div>
<div class="comparison-tabs">
<button
v-for="tab in tabs"
:key="tab.key"
class="tab-btn"
:class="{ active: activeTab === tab.key }"
@click="activeTab = tab.key"
>
{{ tab.label }}
</button>
</div>
<div class="comparison-content">
<transition name="fade" mode="out-in">
<div :key="activeTab" class="tab-content">
<div class="vs-cards">
<div class="vs-card aws-card">
<div class="card-header">
<div class="logo">AWS</div>
<div class="subtitle">Amazon Web Services</div>
</div>
<div class="card-body">
<div
v-for="(point, idx) in currentComparison.aws"
:key="idx"
class="point"
>
<span class="check"></span>
<span>{{ point }}</span>
</div>
</div>
</div>
<div class="vs-divider">
<div class="vs-text">VS</div>
</div>
<div class="vs-card aliyun-card">
<div class="card-header">
<div class="logo aliyun-logo">阿里云</div>
<div class="subtitle">Alibaba Cloud</div>
</div>
<div class="card-body">
<div
v-for="(point, idx) in currentComparison.aliyun"
:key="idx"
class="point"
>
<span class="check aliyun-check"></span>
<span>{{ point }}</span>
</div>
</div>
</div>
</div>
<div class="verdict-box">
<div class="verdict-title">💡 选型建议</div>
<div class="verdict-text">{{ currentComparison.verdict }}</div>
</div>
</div>
</transition>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const activeTab = ref('global')
const tabs = [
{ key: 'global', label: '全球布局' },
{ key: 'ecosystem', label: '生态体系' },
{ key: 'pricing', label: '价格策略' },
{ key: 'enterprise', label: '企业服务' },
{ key: 'developer', label: '开发者体验' }
]
const comparisons = {
global: {
aws: [
'全球 30+ 区域,覆盖率最广',
'发达国家基础设施成熟',
'跨境数据合规经验丰富'
],
aliyun: [
'亚太地区覆盖密度最高',
'中国大陆节点数量领先',
'一带一路区域布局积极'
],
verdict: '出海欧美选 AWS,深耕亚太选阿里云。跨国企业可考虑双云或多云架构。'
},
ecosystem: {
aws: [
'服务种类最丰富(200+ 服务)',
'第三方 SaaS 集成度极高',
'开源生态支持最全面'
],
aliyun: [
'阿里系产品无缝集成',
'电商/零售场景方案成熟',
'国产化替代支持完善'
],
verdict: '技术栈复杂、需丰富组件选 AWS;阿里系业务、电商零售场景选阿里云。'
},
pricing: {
aws: [
'预留实例折扣力度大',
'Spot 竞价实例价格极低',
'免费额度相对保守'
],
aliyun: [
'新用户优惠力度大',
'包年包月性价比高',
'学生/开发者福利多'
],
verdict: '长期稳定负载选 AWS 预留实例;初创公司、预算敏感选阿里云新客优惠。'
},
enterprise: {
aws: [
'企业级支持体系成熟',
'合规认证最全面',
'混合云方案(Outposts'
],
aliyun: [
'本地化服务响应快',
'政府/央企合作深度高',
'专有云/混合云方案完善'
],
verdict: '外企、强合规要求选 AWS;政企客户、需本地化支持选阿里云。'
},
developer: {
aws: [
'文档质量业界标杆',
'认证体系完善',
'社区活跃度最高'
],
aliyun: [
'中文文档详尽',
'学习路径清晰',
'技术社区活跃度高'
],
verdict: '英文好、追求国际认证选 AWS;中文开发者、喜欢中文资料选阿里云。'
}
}
const currentComparison = computed(() => comparisons[activeTab.value])
</script>
<style scoped>
.aws-vs-aliyun-demo {
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
border-radius: 12px;
padding: 24px;
color: #fff;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
.demo-header {
text-align: center;
margin-bottom: 20px;
}
.demo-header h4 {
margin: 0 0 8px 0;
font-size: 1.25rem;
background: linear-gradient(90deg, #00d4ff, #ff9900);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.demo-desc {
margin: 0;
color: #8892b0;
font-size: 0.875rem;
}
.comparison-tabs {
display: flex;
gap: 8px;
margin-bottom: 20px;
overflow-x: auto;
padding-bottom: 4px;
}
.tab-btn {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
color: #8892b0;
padding: 8px 16px;
border-radius: 20px;
cursor: pointer;
font-size: 0.875rem;
white-space: nowrap;
transition: all 0.3s ease;
}
.tab-btn:hover {
background: rgba(255, 255, 255, 0.1);
color: #e6f1ff;
}
.tab-btn.active {
background: linear-gradient(135deg, #00d4ff, #7b2cbf);
border-color: transparent;
color: #fff;
}
.vs-cards {
display: grid;
grid-template-columns: 1fr auto 1fr;
gap: 16px;
margin-bottom: 20px;
}
.vs-card {
background: rgba(255, 255, 255, 0.03);
border-radius: 12px;
padding: 16px;
border: 1px solid rgba(255, 255, 255, 0.08);
}
.aws-card {
border-top: 3px solid #ff9900;
}
.aliyun-card {
border-top: 3px solid #ff6a00;
}
.card-header {
text-align: center;
margin-bottom: 16px;
padding-bottom: 12px;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.logo {
font-size: 1.5rem;
font-weight: 700;
color: #ff9900;
margin-bottom: 4px;
}
.aliyun-logo {
color: #ff6a00;
}
.subtitle {
font-size: 0.75rem;
color: #8892b0;
}
.card-body {
display: flex;
flex-direction: column;
gap: 10px;
}
.point {
display: flex;
align-items: flex-start;
gap: 8px;
font-size: 0.875rem;
color: #e6f1ff;
line-height: 1.5;
}
.check {
color: #ff9900;
font-weight: 700;
flex-shrink: 0;
}
.aliyun-check {
color: #ff6a00;
}
.vs-divider {
display: flex;
align-items: center;
justify-content: center;
}
.vs-text {
background: linear-gradient(135deg, #00d4ff, #7b2cbf);
color: #fff;
width: 44px;
height: 44px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
font-size: 0.875rem;
}
.verdict-box {
background: linear-gradient(135deg, rgba(0, 212, 255, 0.1), rgba(123, 44, 191, 0.1));
border: 1px solid rgba(0, 212, 255, 0.2);
border-radius: 12px;
padding: 16px;
}
.verdict-title {
font-weight: 600;
color: #00d4ff;
margin-bottom: 8px;
font-size: 0.9375rem;
}
.verdict-text {
color: #e6f1ff;
font-size: 0.875rem;
line-height: 1.6;
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s ease, transform 0.3s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
transform: translateY(10px);
}
@media (max-width: 768px) {
.vs-cards {
grid-template-columns: 1fr;
gap: 12px;
}
.vs-divider {
display: none;
}
.comparison-tabs {
gap: 6px;
}
.tab-btn {
padding: 6px 12px;
font-size: 0.8rem;
}
}
</style>
@@ -0,0 +1,590 @@
<template>
<div class="cloud-services-map-demo">
<div class="demo-header">
<h4>云计算服务版图全景图</h4>
<p class="demo-desc">点击各个板块查看 AWS 与阿里云的对应服务</p>
</div>
<div class="map-container">
<!-- 计算层 -->
<div
class="service-layer compute-layer"
:class="{ active: activeLayer === 'compute' }"
@click="setActiveLayer('compute')"
>
<div class="layer-icon"></div>
<div class="layer-title">计算服务</div>
<div class="layer-services">
<span class="service-tag">EC2/ECS</span>
<span class="service-tag">Lambda/函数计算</span>
</div>
</div>
<!-- 存储层 -->
<div
class="service-layer storage-layer"
:class="{ active: activeLayer === 'storage' }"
@click="setActiveLayer('storage')"
>
<div class="layer-icon">💾</div>
<div class="layer-title">存储服务</div>
<div class="layer-services">
<span class="service-tag">S3/OSS</span>
<span class="service-tag">EBS/云盘</span>
</div>
</div>
<!-- 网络层 -->
<div
class="service-layer network-layer"
:class="{ active: activeLayer === 'network' }"
@click="setActiveLayer('network')"
>
<div class="layer-icon">🌐</div>
<div class="layer-title">网络服务</div>
<div class="layer-services">
<span class="service-tag">VPC/专有网络</span>
<span class="service-tag">ELB/SLB</span>
</div>
</div>
<!-- 安全层 -->
<div
class="service-layer security-layer"
:class="{ active: activeLayer === 'security' }"
@click="setActiveLayer('security')"
>
<div class="layer-icon">🔒</div>
<div class="layer-title">安全服务</div>
<div class="layer-services">
<span class="service-tag">IAM/RAM</span>
<span class="service-tag">KMS/密钥管理</span>
</div>
</div>
<!-- 数据库层 -->
<div
class="service-layer database-layer"
:class="{ active: activeLayer === 'database' }"
@click="setActiveLayer('database')"
>
<div class="layer-icon">🗄</div>
<div class="layer-title">数据库服务</div>
<div class="layer-services">
<span class="service-tag">RDS/PolarDB</span>
<span class="service-tag">DynamoDB/Tablestore</span>
</div>
</div>
<!-- 中间件层 -->
<div
class="service-layer middleware-layer"
:class="{ active: activeLayer === 'middleware' }"
@click="setActiveLayer('middleware')"
>
<div class="layer-icon">🔧</div>
<div class="layer-title">中间件服务</div>
<div class="layer-services">
<span class="service-tag">MQ/RocketMQ</span>
<span class="service-tag">ElastiCache/Redis</span>
</div>
</div>
</div>
<!-- 详情面板 -->
<div v-if="activeLayer" class="detail-panel">
<div class="detail-header">
<h5>{{ layerDetails[activeLayer].title }}</h5>
<button class="close-btn" @click="activeLayer = null">×</button>
</div>
<div class="detail-content">
<div class="comparison-table">
<div class="table-header">
<div class="col aws">AWS</div>
<div class="col aliyun">阿里云</div>
<div class="col desc">功能描述</div>
</div>
<div
v-for="(item, index) in layerDetails[activeLayer].services"
:key="index"
class="table-row"
>
<div class="col aws">{{ item.aws }}</div>
<div class="col aliyun">{{ item.aliyun }}</div>
<div class="col desc">{{ item.desc }}</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const activeLayer = ref(null)
const setActiveLayer = (layer) => {
activeLayer.value = layer
}
const layerDetails = {
compute: {
title: '计算服务对比',
services: [
{
aws: 'Amazon EC2',
aliyun: 'ECS 云服务器',
desc: '虚拟服务器,可完全控制计算资源'
},
{
aws: 'AWS Lambda',
aliyun: '函数计算 FC',
desc: '无服务器计算,按需运行代码'
},
{
aws: 'Amazon ECS/EKS',
aliyun: 'ACK 容器服务',
desc: '容器编排和管理服务'
},
{
aws: 'AWS Fargate',
aliyun: 'Serverless Kubernetes',
desc: '无服务器容器计算引擎'
},
{
aws: 'AWS Batch',
aliyun: '批量计算',
desc: '批量作业调度服务'
},
{
aws: 'AWS Elastic Beanstalk',
aliyun: 'EDAS',
desc: '应用部署和托管平台'
}
]
},
storage: {
title: '存储服务对比',
services: [
{
aws: 'Amazon S3',
aliyun: 'OSS 对象存储',
desc: '海量、安全、低成本的对象存储'
},
{
aws: 'Amazon EBS',
aliyun: '云盘 ESSD',
desc: '块存储服务,为EC2/ECS提供持久存储'
},
{
aws: 'Amazon EFS',
aliyun: 'NAS 文件存储',
desc: '托管的弹性文件存储'
},
{
aws: 'Amazon Glacier',
aliyun: 'OSS 归档存储',
desc: '低成本长期归档存储'
},
{
aws: 'AWS Storage Gateway',
aliyun: '混合云存储阵列',
desc: '混合云存储服务'
},
{
aws: 'AWS Backup',
aliyun: '云备份服务',
desc: '集中式备份管理'
}
]
},
network: {
title: '网络服务对比',
services: [
{
aws: 'Amazon VPC',
aliyun: '专有网络 VPC',
desc: '虚拟私有云网络环境'
},
{
aws: 'Elastic Load Balancing',
aliyun: 'SLB 负载均衡',
desc: '流量分发服务'
},
{
aws: 'Amazon CloudFront',
aliyun: 'CDN 内容分发',
desc: '全球内容分发网络'
},
{
aws: 'AWS Transit Gateway',
aliyun: '云企业网 CEN',
desc: '网络传输网关'
},
{
aws: 'AWS Direct Connect',
aliyun: '高速通道',
desc: '专线连接服务'
},
{
aws: 'AWS App Mesh',
aliyun: '服务网格 ASM',
desc: '微服务网格管理'
},
{
aws: 'AWS Global Accelerator',
aliyun: '全球加速 GA',
desc: '网络加速服务'
}
]
},
security: {
title: '安全服务对比',
services: [
{
aws: 'AWS IAM',
aliyun: 'RAM 访问控制',
desc: '身份和访问管理服务'
},
{
aws: 'AWS KMS',
aliyun: 'KMS 密钥管理',
desc: '密钥管理服务'
},
{
aws: 'AWS WAF',
aliyun: 'WAF 防火墙',
desc: 'Web应用防火墙'
},
{
aws: 'AWS Shield',
aliyun: 'DDoS 防护',
desc: 'DDoS攻击防护'
},
{
aws: 'Amazon GuardDuty',
aliyun: '云安全中心',
desc: '智能威胁检测'
},
{
aws: 'AWS Certificate Manager',
aliyun: 'SSL 证书服务',
desc: 'SSL/TLS证书管理'
},
{
aws: 'AWS Secrets Manager',
aliyun: '凭据管家',
desc: '机密信息托管'
},
{
aws: 'Amazon Macie',
aliyun: '敏感数据保护',
desc: '敏感数据发现与保护'
}
]
},
database: {
title: '数据库服务对比',
services: [
{
aws: 'Amazon RDS',
aliyun: 'RDS 关系型数据库',
desc: '托管的关系型数据库服务'
},
{
aws: 'Amazon Aurora',
aliyun: 'PolarDB',
desc: '云原生关系型数据库'
},
{
aws: 'Amazon DynamoDB',
aliyun: 'Tablestore',
desc: 'NoSQL键值和文档数据库'
},
{
aws: 'Amazon ElastiCache',
aliyun: '云数据库 Redis',
desc: '托管的内存缓存服务'
},
{
aws: 'Amazon DocumentDB',
aliyun: 'MongoDB 副本集',
desc: '兼容MongoDB的文档数据库'
},
{
aws: 'Amazon Keyspaces',
aliyun: 'Cassandra 服务',
desc: '托管的Cassandra兼容服务'
},
{
aws: 'Amazon Neptune',
aliyun: '图数据库 GDB',
desc: '全托管图数据库'
},
{
aws: 'Amazon QLDB',
aliyun: '区块链 BaaS',
desc: '全托管分类账数据库'
},
{
aws: 'Amazon Timestream',
aliyun: '时序数据库 TSDB',
desc: '全托管时序数据库'
}
]
},
middleware: {
title: '中间件服务对比',
services: [
{
aws: 'Amazon MQ',
aliyun: '消息队列 MQ',
desc: '托管的消息代理服务'
},
{
aws: 'Amazon SQS',
aliyun: '消息服务 MNS',
desc: '全托管消息队列服务'
},
{
aws: 'Amazon SNS',
aliyun: '事件总线 EventBridge',
desc: '全托管发布/订阅服务'
},
{
aws: 'Amazon Kinesis',
aliyun: '实时计算 Flink',
desc: '实时数据流处理'
},
{
aws: 'AWS Step Functions',
aliyun: 'Serverless 工作流',
desc: '工作流编排服务'
},
{
aws: 'AWS AppSync',
aliyun: 'API 网关',
desc: '托管GraphQL服务'
},
{
aws: 'Amazon EventBridge',
aliyun: '事件总线',
desc: '无服务器事件总线'
}
]
}
}
</script>
<style scoped>
.cloud-services-map-demo {
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
border-radius: 12px;
padding: 24px;
color: #fff;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
.demo-header {
text-align: center;
margin-bottom: 24px;
}
.demo-header h4 {
margin: 0 0 8px 0;
font-size: 1.25rem;
background: linear-gradient(90deg, #00d4ff, #7b2cbf);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.demo-desc {
margin: 0;
color: #8892b0;
font-size: 0.875rem;
}
.map-container {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
margin-bottom: 20px;
}
.service-layer {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
padding: 16px;
text-align: center;
cursor: pointer;
transition: all 0.3s ease;
}
.service-layer:hover {
background: rgba(255, 255, 255, 0.1);
transform: translateY(-2px);
}
.service-layer.active {
background: rgba(0, 212, 255, 0.15);
border-color: #00d4ff;
box-shadow: 0 0 20px rgba(0, 212, 255, 0.3);
}
.layer-icon {
font-size: 2rem;
margin-bottom: 8px;
}
.layer-title {
font-weight: 600;
font-size: 0.9375rem;
margin-bottom: 8px;
color: #e6f1ff;
}
.layer-services {
display: flex;
flex-wrap: wrap;
gap: 4px;
justify-content: center;
}
.service-tag {
background: rgba(123, 44, 191, 0.3);
color: #c084fc;
padding: 2px 8px;
border-radius: 12px;
font-size: 0.75rem;
}
.service-layer.active .service-tag {
background: rgba(0, 212, 255, 0.3);
color: #00d4ff;
}
.detail-panel {
background: rgba(0, 0, 0, 0.3);
border-radius: 12px;
padding: 20px;
margin-top: 16px;
animation: slideDown 0.3s ease;
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.detail-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
padding-bottom: 12px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.detail-header h5 {
margin: 0;
color: #00d4ff;
font-size: 1.1rem;
}
.close-btn {
background: none;
border: none;
color: #8892b0;
font-size: 1.5rem;
cursor: pointer;
padding: 0;
width: 28px;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
transition: all 0.2s;
}
.close-btn:hover {
background: rgba(255, 255, 255, 0.1);
color: #fff;
}
.comparison-table {
width: 100%;
}
.table-header {
display: grid;
grid-template-columns: 1.2fr 1.2fr 2fr;
gap: 12px;
padding: 10px 12px;
background: rgba(0, 212, 255, 0.1);
border-radius: 8px;
font-weight: 600;
font-size: 0.875rem;
color: #e6f1ff;
margin-bottom: 8px;
}
.table-row {
display: grid;
grid-template-columns: 1.2fr 1.2fr 2fr;
gap: 12px;
padding: 10px 12px;
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
font-size: 0.875rem;
transition: background 0.2s;
}
.table-row:hover {
background: rgba(255, 255, 255, 0.03);
border-radius: 6px;
}
.col.aws {
color: #ff9900;
font-weight: 500;
}
.col.aliyun {
color: #ff6a00;
font-weight: 500;
}
.col.desc {
color: #8892b0;
}
@media (max-width: 768px) {
.map-container {
grid-template-columns: repeat(2, 1fr);
}
.table-header,
.table-row {
grid-template-columns: 1fr 1fr;
}
.col.desc {
display: none;
}
}
@media (max-width: 480px) {
.map-container {
grid-template-columns: 1fr;
}
}
</style>
@@ -0,0 +1,463 @@
<template>
<div class="compute-services-demo">
<div class="demo-header">
<h4>计算服务选型指南</h4>
<p class="demo-desc">拖动滑块调整场景参数获取最佳计算方案</p>
</div>
<div class="scenario-sliders">
<div class="slider-group">
<label>负载稳定性</label>
<input
v-model.number="scenario.stability"
type="range"
min="0"
max="100"
/>
<div class="slider-labels">
<span>波动大</span>
<span>非常稳定</span>
</div>
</div>
<div class="slider-group">
<label>平均负载率</label>
<input
v-model.number="scenario.utilization"
type="range"
min="0"
max="100"
/>
<div class="slider-labels">
<span>很低</span>
<span>接近100%</span>
</div>
</div>
<div class="slider-group">
<label>任务持续时间</label>
<input
v-model.number="scenario.duration"
type="range"
min="0"
max="100"
/>
<div class="slider-labels">
<span>几分钟</span>
<span>持续运行</span>
</div>
</div>
<div class="slider-group">
<label>流量突发程度</label>
<input
v-model.number="scenario.burstiness"
type="range"
min="0"
max="100"
/>
<div class="slider-labels">
<span>平稳</span>
<span>大起大落</span>
</div>
</div>
</div>
<div class="recommendation-panel">
<div class="recommendation-title">
<span class="icon">🎯</span>
推荐方案
</div>
<div class="solution-cards">
<div
v-for="(solution, index) in recommendations"
:key="index"
class="solution-card"
:class="{ 'top-pick': index === 0 }"
>
<div class="solution-rank" :class="{ 'rank-1': index === 0 }">
{{ index === 0 ? '👑' : index + 1 }}
</div>
<div class="solution-content">
<div class="solution-name">{{ solution.name }}</div>
<div class="solution-services">
<span class="service-tag aws">{{ solution.aws }}</span>
<span class="vs-mini">vs</span>
<span class="service-tag aliyun">{{ solution.aliyun }}</span>
</div>
<div class="solution-reason">{{ solution.reason }}</div>
<div class="solution-savings" v-if="solution.savings">
💰 预计节省: {{ solution.savings }}
</div>
</div>
</div>
</div>
</div>
<div class="scenario-presets">
<span class="preset-label">快速场景:</span>
<button
v-for="preset in presets"
:key="preset.name"
class="preset-btn"
@click="applyPreset(preset)"
>
{{ preset.name }}
</button>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const scenario = ref({
stability: 50,
utilization: 60,
duration: 70,
burstiness: 30
})
const presets = [
{
name: '电商大促',
values: { stability: 20, utilization: 40, duration: 90, burstiness: 90 }
},
{
name: '企业内部系统',
values: { stability: 90, utilization: 70, duration: 95, burstiness: 10 }
},
{
name: '初创公司官网',
values: { stability: 40, utilization: 20, duration: 80, burstiness: 30 }
},
{
name: '数据处理任务',
values: { stability: 30, utilization: 95, duration: 10, burstiness: 80 }
},
{
name: 'SaaS 平台',
values: { stability: 60, utilization: 50, duration: 95, burstiness: 60 }
}
]
const applyPreset = (preset) => {
scenario.value = { ...preset.values }
}
const recommendations = computed(() => {
const s = scenario.value
const solutions = []
// 计算各方案得分
let serverlessScore = 0
let ec2Score = 0
let spotScore = 0
let reservedScore = 0
// Serverless (Lambda/FC)
if (s.duration < 30) serverlessScore += 30
if (s.burstiness > 70) serverlessScore += 25
if (s.utilization < 30) serverlessScore += 20
if (s.stability < 30) serverlessScore += 15
// Spot 实例
if (s.burstiness > 60) spotScore += 25
if (s.stability < 40) spotScore += 30
if (s.duration < 40) spotScore += 20
if (s.utilization < 50) spotScore += 15
// 预留实例
if (s.stability > 70) reservedScore += 35
if (s.duration > 80) reservedScore += 25
if (s.utilization > 60) reservedScore += 20
if (s.burstiness < 30) reservedScore += 10
// 按需实例 (兜底)
ec2Score = 40
// 排序并生成推荐
const scores = [
{ type: 'serverless', score: serverlessScore, savings: '40-70%' },
{ type: 'spot', score: spotScore, savings: '60-90%' },
{ type: 'reserved', score: reservedScore, savings: '30-60%' },
{ type: 'ondemand', score: ec2Score, savings: null }
].sort((a, b) => b.score - a.score)
const solutionMap = {
serverless: {
name: '无服务器架构',
aws: 'AWS Lambda',
aliyun: '函数计算 FC',
reason: '流量波动大、任务短时,按调用计费最划算,自动扩缩容免运维'
},
spot: {
name: '竞价实例',
aws: 'EC2 Spot',
aliyun: '抢占式实例',
reason: '可容忍中断的计算任务,价格极低,适合批处理、渲染等场景'
},
reserved: {
name: '预留实例',
aws: 'Reserved Instances',
aliyun: '包年包月',
reason: '长期稳定负载,提前承诺使用时长换取大幅折扣,成本最优'
},
ondemand: {
name: '按需实例',
aws: 'EC2 On-Demand',
aliyun: '按量付费 ECS',
reason: '灵活性最高,按小时计费,适合测试环境或 unpredictable 负载'
}
}
return scores.slice(0, 3).map((s, idx) => ({
...solutionMap[s.type],
savings: s.savings
}))
})
</script>
<style scoped>
.compute-services-demo {
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
border-radius: 12px;
padding: 24px;
color: #fff;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
.demo-header {
text-align: center;
margin-bottom: 24px;
}
.demo-header h4 {
margin: 0 0 8px 0;
font-size: 1.25rem;
background: linear-gradient(90deg, #00d4ff, #7b2cbf);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.demo-desc {
margin: 0;
color: #8892b0;
font-size: 0.875rem;
}
.scenario-sliders {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px;
margin-bottom: 24px;
}
.slider-group {
background: rgba(255, 255, 255, 0.03);
border-radius: 8px;
padding: 12px;
}
.slider-group label {
display: block;
font-size: 0.875rem;
color: #e6f1ff;
margin-bottom: 8px;
font-weight: 500;
}
.slider-group input[type='range'] {
width: 100%;
height: 6px;
background: rgba(255, 255, 255, 0.1);
border-radius: 3px;
outline: none;
-webkit-appearance: none;
margin-bottom: 8px;
}
.slider-group input[type='range']::-webkit-slider-thumb {
-webkit-appearance: none;
width: 18px;
height: 18px;
background: linear-gradient(135deg, #00d4ff, #7b2cbf);
border-radius: 50%;
cursor: pointer;
box-shadow: 0 0 10px rgba(0, 212, 255, 0.5);
}
.slider-labels {
display: flex;
justify-content: space-between;
font-size: 0.75rem;
color: #8892b0;
}
.recommendation-panel {
background: rgba(0, 0, 0, 0.2);
border-radius: 12px;
padding: 20px;
margin-bottom: 16px;
}
.recommendation-title {
font-size: 1rem;
font-weight: 600;
color: #e6f1ff;
margin-bottom: 16px;
display: flex;
align-items: center;
gap: 8px;
}
.icon {
font-size: 1.25rem;
}
.solution-cards {
display: flex;
flex-direction: column;
gap: 12px;
}
.solution-card {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 10px;
padding: 16px;
display: flex;
gap: 12px;
transition: all 0.3s ease;
}
.solution-card:hover {
background: rgba(255, 255, 255, 0.08);
}
.solution-card.top-pick {
background: linear-gradient(135deg, rgba(0, 212, 255, 0.15), rgba(123, 44, 191, 0.15));
border-color: rgba(0, 212, 255, 0.3);
}
.solution-rank {
width: 36px;
height: 36px;
background: rgba(255, 255, 255, 0.1);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
font-size: 0.875rem;
color: #8892b0;
flex-shrink: 0;
}
.solution-rank.rank-1 {
background: linear-gradient(135deg, #ffd700, #ffaa00);
color: #1a1a2e;
font-size: 1.25rem;
}
.solution-content {
flex: 1;
}
.solution-name {
font-weight: 600;
font-size: 1rem;
color: #e6f1ff;
margin-bottom: 6px;
}
.solution-services {
display: flex;
align-items: center;
gap: 6px;
margin-bottom: 8px;
flex-wrap: wrap;
}
.service-tag {
padding: 2px 8px;
border-radius: 4px;
font-size: 0.75rem;
font-weight: 500;
}
.service-tag.aws {
background: rgba(255, 153, 0, 0.2);
color: #ff9900;
}
.service-tag.aliyun {
background: rgba(255, 106, 0, 0.2);
color: #ff6a00;
}
.vs-mini {
color: #8892b0;
font-size: 0.75rem;
}
.solution-reason {
font-size: 0.875rem;
color: #8892b0;
line-height: 1.5;
}
.solution-savings {
margin-top: 8px;
font-size: 0.8125rem;
color: #00d4ff;
font-weight: 500;
}
.scenario-presets {
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: center;
padding-top: 16px;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.preset-label {
font-size: 0.875rem;
color: #8892b0;
margin-right: 8px;
}
.preset-btn {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
color: #e6f1ff;
padding: 6px 14px;
border-radius: 16px;
cursor: pointer;
font-size: 0.8125rem;
transition: all 0.2s ease;
}
.preset-btn:hover {
background: rgba(255, 255, 255, 0.1);
border-color: rgba(255, 255, 255, 0.2);
}
@media (max-width: 768px) {
.scenario-sliders {
grid-template-columns: 1fr;
}
.solution-card {
flex-direction: column;
}
.scenario-presets {
justify-content: flex-start;
}
}
</style>
@@ -0,0 +1,289 @@
<template>
<div class="database-services-demo">
<div class="demo-header">
<h4>数据库选型助手</h4>
<p class="demo-desc">根据您的业务特点推荐最适合的数据库方案</p>
</div>
<div class="db-selection">
<div class="db-categories">
<button
v-for="cat in categories"
:key="cat.id"
class="cat-btn"
:class="{ active: selectedCategory === cat.id }"
@click="selectCategory(cat.id)"
>
<span class="cat-icon">{{ cat.icon }}</span>
<span class="cat-name">{{ cat.name }}</span>
</button>
</div>
<div v-if="selectedCategory" class="db-comparison">
<div class="comparison-header">
<span class="aws-badge">AWS</span>
<span class="vs-text">对比</span>
<span class="aliyun-badge">阿里云</span>
</div>
<div class="db-cards">
<div class="db-card">
<div class="db-header aws">
<div class="db-name">{{ currentCategory.aws }}</div>
</div>
<div class="db-body">
<div class="feature-list">
<div v-for="(feat, i) in currentCategory.awsFeatures" :key="i" class="feature">
{{ feat }}
</div>
</div>
<div class="price-tag">{{ currentCategory.awsPrice }}</div>
</div>
</div>
<div class="db-card">
<div class="db-header aliyun">
<div class="db-name">{{ currentCategory.aliyun }}</div>
</div>
<div class="db-body">
<div class="feature-list">
<div v-for="(feat, i) in currentCategory.aliyunFeatures" :key="i" class="feature">
{{ feat }}
</div>
</div>
<div class="price-tag aliyun">{{ currentCategory.aliyunPrice }}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const selectedCategory = ref('relational')
const categories = [
{ id: 'relational', name: '关系型数据库', icon: '📊' },
{ id: 'nosql', name: 'NoSQL 数据库', icon: '📦' },
{ id: 'cache', name: '缓存服务', icon: '⚡' },
{ id: 'analytics', name: '分析型数据库', icon: '📈' }
]
const categoryData = {
relational: {
aws: 'Amazon RDS / Aurora',
aliyun: 'RDS / PolarDB',
awsFeatures: ['MySQL/PostgreSQL/Oracle/SQL Server 支持', 'Aurora 5 倍性能提升', '自动故障转移和读副本', 'Serverless 自动扩缩容'],
aliyunFeatures: ['MySQL/SQL Server/PostgreSQL/Oracle 支持', 'PolarDB 计算存储分离', '秒级备份恢复', 'Oracle 语法兼容模式'],
awsPrice: '$0.017/小时起',
aliyunPrice: '¥0.12/小时起'
},
nosql: {
aws: 'Amazon DynamoDB',
aliyun: 'Tablestore',
awsFeatures: ['全托管 NoSQL 键值和文档数据库', '单表设计支持 PB 级规模', 'DAX 内存缓存加速', '全局表多区域复制'],
aliyunFeatures: ['分布式 NoSQL 数据库存储', '自动分片和负载均衡', '二级索引和全文检索', '毫秒级单点读写延迟'],
awsPrice: '按需 $1.25/百万次写',
aliyunPrice: '按量 0.4元/万次写'
},
cache: {
aws: 'Amazon ElastiCache',
aliyun: '云数据库 Redis',
awsFeatures: ['托管 Redis 和 Memcached', '集群模式自动分片', '只读副本和自动故障转移', '备份恢复和快照'],
aliyunFeatures: ['主从双节点架构', '自动故障切换', '读写分离能力', '数据持久化备份'],
awsPrice: '$0.012/小时起',
aliyunPrice: '¥0.08/小时起'
},
analytics: {
aws: 'Amazon Redshift',
aliyun: 'AnalyticDB',
awsFeatures: ['PB 级数据仓库', '列式存储和压缩', 'Spectrum 查询 S3 数据', '并发扩展和自动优化'],
aliyunFeatures: ['实时分析型数据库', 'MPP 大规模并行处理', '高并发低延迟查询', '自动索引和优化'],
awsPrice: '$0.25/小时起',
aliyunPrice: '¥2.0/小时起'
}
}
const selectCategory = (id) => {
selectedCategory.value = id
}
const currentCategory = computed(() => categoryData[selectedCategory.value])
</script>
<style scoped>
.database-services-demo {
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
border-radius: 12px;
padding: 24px;
color: #fff;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
.demo-header {
text-align: center;
margin-bottom: 24px;
}
.demo-header h4 {
margin: 0 0 8px 0;
font-size: 1.25rem;
background: linear-gradient(90deg, #00d4ff, #7b2cbf);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.demo-desc {
margin: 0;
color: #8892b0;
font-size: 0.875rem;
}
.db-selection {
background: rgba(0, 0, 0, 0.2);
border-radius: 12px;
padding: 20px;
}
.db-categories {
display: flex;
gap: 8px;
margin-bottom: 20px;
flex-wrap: wrap;
}
.cat-btn {
display: flex;
align-items: center;
gap: 6px;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
color: #e6f1ff;
padding: 8px 16px;
border-radius: 20px;
cursor: pointer;
font-size: 0.875rem;
transition: all 0.2s ease;
}
.cat-btn:hover {
background: rgba(255, 255, 255, 0.1);
}
.cat-btn.active {
background: linear-gradient(135deg, #00d4ff, #7b2cbf);
border-color: transparent;
color: #fff;
}
.cat-icon {
font-size: 1rem;
}
.comparison-header {
display: flex;
align-items: center;
justify-content: center;
gap: 16px;
margin-bottom: 16px;
padding-bottom: 12px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.aws-badge, .aliyun-badge {
padding: 6px 14px;
border-radius: 16px;
font-size: 0.8125rem;
font-weight: 600;
}
.aws-badge {
background: rgba(255, 153, 0, 0.2);
color: #ff9900;
}
.aliyun-badge {
background: rgba(255, 106, 0, 0.2);
color: #ff6a00;
}
.vs-text {
color: #8892b0;
font-size: 0.75rem;
}
.db-cards {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
}
.db-card {
background: rgba(0, 0, 0, 0.2);
border-radius: 10px;
overflow: hidden;
}
.db-header {
padding: 12px 16px;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.db-header.aws {
background: rgba(255, 153, 0, 0.1);
}
.db-header.aliyun {
background: rgba(255, 106, 0, 0.1);
}
.db-name {
font-size: 1rem;
font-weight: 600;
color: #e6f1ff;
}
.db-body {
padding: 16px;
}
.feature-list {
margin-bottom: 12px;
}
.feature {
font-size: 0.8125rem;
color: #e6f1ff;
padding: 4px 0;
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}
.price-tag {
background: rgba(0, 212, 255, 0.1);
color: #00d4ff;
padding: 6px 12px;
border-radius: 6px;
font-size: 0.8125rem;
font-weight: 500;
text-align: center;
}
.price-tag.aliyun {
color: #ff6a00;
background: rgba(255, 106, 0, 0.1);
}
@media (max-width: 768px) {
.db-categories {
justify-content: center;
}
.db-cards {
grid-template-columns: 1fr;
}
}
</style>
@@ -0,0 +1,223 @@
<template>
<div class="k8s-services-demo">
<div class="demo-header">
<h4>Kubernetes 服务生态全景</h4>
<p class="demo-desc">探索 AWS 和阿里云上的 K8s 服务及配套生态</p>
</div>
<div class="k8s-architecture">
<div class="arch-layer control-plane">
<div class="layer-title">控制平面</div>
<div class="layer-content">
<div class="service-box">
<div class="service-name">EKS / ACK</div>
<div class="service-desc">托管 Kubernetes 控制平面</div>
</div>
</div>
</div>
<div class="arch-layer worker-nodes">
<div class="layer-title">工作节点</div>
<div class="layer-content">
<div class="node-types">
<div class="node-box">
<div class="node-icon">💻</div>
<div class="node-name">EC2/ECS</div>
<div class="node-desc">标准计算节点</div>
</div>
<div class="node-box">
<div class="node-icon"></div>
<div class="node-name">Fargate/ECI</div>
<div class="node-desc">Serverless 节点</div>
</div>
<div class="node-box">
<div class="node-icon">🎯</div>
<div class="node-name">Spot/抢占式</div>
<div class="node-desc">低成本竞价节点</div>
</div>
</div>
</div>
</div>
<div class="arch-layer addons">
<div class="layer-title">插件生态</div>
<div class="layer-content">
<div class="addon-grid">
<div class="addon-card">
<div class="addon-name">Ingress/Nginx</div>
<div class="addon-aws">AWS Load Balancer</div>
<div class="addon-aliyun">ALB Ingress</div>
</div>
<div class="addon-card">
<div class="addon-name">Storage</div>
<div class="addon-aws">EBS/EFS CSI</div>
<div class="addon-aliyun">云盘/NAS CSI</div>
</div>
<div class="addon-card">
<div class="addon-name">Monitoring</div>
<div class="addon-aws">CloudWatch/AMP</div>
<div class="addon-aliyun">ARMS/Prometheus</div>
</div>
<div class="addon-card">
<div class="addon-name">Service Mesh</div>
<div class="addon-aws">App Mesh</div>
<div class="addon-aliyun">Service Mesh ASM</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
// Component logic here if needed
</script>
<style scoped>
.k8s-services-demo {
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
border-radius: 12px;
padding: 24px;
color: #fff;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
.demo-header {
text-align: center;
margin-bottom: 24px;
}
.demo-header h4 {
margin: 0 0 8px 0;
font-size: 1.25rem;
background: linear-gradient(90deg, #00d4ff, #7b2cbf);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.demo-desc {
margin: 0;
color: #8892b0;
font-size: 0.875rem;
}
.k8s-architecture {
display: flex;
flex-direction: column;
gap: 16px;
}
.arch-layer {
background: rgba(255, 255, 255, 0.03);
border-radius: 12px;
padding: 16px;
border: 1px solid rgba(255, 255, 255, 0.08);
}
.layer-title {
font-weight: 600;
font-size: 0.875rem;
color: #00d4ff;
margin-bottom: 12px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.service-box {
background: linear-gradient(135deg, rgba(0, 212, 255, 0.15), rgba(123, 44, 191, 0.15));
border: 1px solid rgba(0, 212, 255, 0.2);
border-radius: 10px;
padding: 16px;
text-align: center;
}
.service-name {
font-size: 1.25rem;
font-weight: 700;
color: #e6f1ff;
margin-bottom: 4px;
}
.service-desc {
font-size: 0.8125rem;
color: #8892b0;
}
.node-types {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 12px;
}
.node-box {
background: rgba(0, 0, 0, 0.2);
border-radius: 10px;
padding: 14px;
text-align: center;
border: 1px solid rgba(255, 255, 255, 0.08);
}
.node-icon {
font-size: 1.5rem;
margin-bottom: 6px;
}
.node-name {
font-size: 0.8125rem;
font-weight: 600;
color: #e6f1ff;
margin-bottom: 2px;
}
.node-desc {
font-size: 0.6875rem;
color: #8892b0;
}
.addon-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 10px;
}
.addon-card {
background: rgba(0, 0, 0, 0.2);
border-radius: 8px;
padding: 12px;
border: 1px solid rgba(255, 255, 255, 0.05);
}
.addon-name {
font-size: 0.875rem;
font-weight: 600;
color: #e6f1ff;
margin-bottom: 8px;
padding-bottom: 6px;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.addon-aws, .addon-aliyun {
font-size: 0.75rem;
padding: 2px 0;
}
.addon-aws {
color: #ff9900;
}
.addon-aliyun {
color: #ff6a00;
}
@media (max-width: 768px) {
.node-types {
grid-template-columns: 1fr;
}
.addon-grid {
grid-template-columns: 1fr;
}
}
</style>
@@ -0,0 +1,637 @@
<template>
<div class="network-services-demo">
<div class="demo-header">
<h4>网络架构可视化配置</h4>
<p class="demo-desc">拖拽组件构建您的云上网络架构</p>
</div>
<div class="network-builder">
<div class="components-panel">
<div class="panel-title">可用组件</div>
<div class="component-list">
<div
v-for="component in networkComponents"
:key="component.id"
class="component-item"
draggable="true"
@dragstart="onDragStart($event, component)"
>
<span class="component-icon">{{ component.icon }}</span>
<span class="component-name">{{ component.name }}</span>
</div>
</div>
</div>
<div class="canvas-area">
<div
class="network-canvas"
@drop="onDrop"
@dragover.prevent
>
<div v-if="canvasItems.length === 0" class="empty-state">
<div class="empty-icon">🏗</div>
<div class="empty-text">拖拽左侧组件到此处</div>
<div class="empty-subtext">开始构建您的网络架构</div>
</div>
<div
v-for="(item, index) in canvasItems"
:key="item.id"
class="canvas-item"
:class="item.type"
:style="itemStyle(index)"
@click="selectItem(item)"
>
<div class="item-icon">{{ item.icon }}</div>
<div class="item-name">{{ item.name }}</div>
<button class="remove-btn" @click.stop="removeItem(index)">×</button>
</div>
</div>
</div>
</div>
<div v-if="selectedItem" class="config-panel">
<div class="config-header">
<span class="config-icon">{{ selectedItem.icon }}</span>
<span class="config-title">{{ selectedItem.name }} 配置</span>
<button class="close-config" @click="selectedItem = null">×</button>
</div>
<div class="config-content">
<div class="config-section">
<div class="section-title">AWS 配置</div>
<div class="service-name">{{ selectedItem.awsService }}</div>
<div class="config-options">
<div
v-for="(option, idx) in selectedItem.awsOptions"
:key="idx"
class="option-item"
>
<span class="option-check"></span>
<span>{{ option }}</span>
</div>
</div>
</div>
<div class="config-divider"></div>
<div class="config-section">
<div class="section-title aliyun-title">阿里云配置</div>
<div class="service-name aliyun-service">{{ selectedItem.aliyunService }}</div>
<div class="config-options">
<div
v-for="(option, idx) in selectedItem.aliyunOptions"
:key="idx"
class="option-item"
>
<span class="option-check aliyun-check"></span>
<span>{{ option }}</span>
</div>
</div>
</div>
</div>
<div class="config-footer">
<div class="price-compare">
<div class="price-item">
<span class="price-label">AWS:</span>
<span class="price-value">{{ selectedItem.awsPrice }}</span>
</div>
<div class="price-item">
<span class="price-label">阿里云:</span>
<span class="price-value aliyun-price">{{ selectedItem.aliyunPrice }}</span>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const canvasItems = ref([])
const selectedItem = ref(null)
let draggedItem = null
const networkComponents = [
{
id: 'vpc',
name: '专有网络',
icon: '🏠',
type: 'network',
awsService: 'Amazon VPC',
aliyunService: '专有网络 VPC',
awsOptions: [
'自定义 IP 地址范围',
'多可用区子网划分',
'网络 ACL 和安全组',
'VPC 对等连接和 Transit Gateway'
],
aliyunOptions: [
'自定义私网网段',
'交换机跨可用区部署',
'安全组和网络 ACL',
'VPC 互通和云企业网'
],
awsPrice: '免费(子网内流量)',
aliyunPrice: '免费(同 VPC 内流量)'
},
{
id: 'cdn',
name: '内容分发',
icon: '🚀',
type: 'network',
awsService: 'Amazon CloudFront',
aliyunService: 'CDN 内容分发',
awsOptions: [
'全球 400+ 边缘节点',
'支持静态和动态内容加速',
'Lambda@Edge 边缘计算',
'与 AWS Shield 集成防护'
],
aliyunOptions: [
'国内 2800+ 节点覆盖',
'全站加速和下载分发',
'边缘脚本和缓存优化',
'与 WAF 联动安全防护'
],
awsPrice: 'HTTP: $0.085/GB 起',
aliyunPrice: 'HTTP: ¥0.15/GB 起'
},
{
id: 'lb',
name: '负载均衡',
icon: '⚖️',
type: 'network',
awsService: 'Elastic Load Balancing',
aliyunService: 'SLB 负载均衡',
awsOptions: [
'ALB/NLB/CLB 多种类型',
'自动健康检查和故障转移',
'SSL/TLS 终止和证书管理',
'与 Auto Scaling 集成'
],
aliyunOptions: [
'ALB/NLB/CLB 全类型支持',
'主备和集群高可用模式',
'HTTPS 证书一键部署',
'与 ESS 弹性伸缩联动'
],
awsPrice: 'ALB: $0.0225/小时 + LCU',
aliyunPrice: 'ALB: ¥0.15/小时 + LCU'
},
{
id: 'waf',
name: 'WAF 防火墙',
icon: '🛡️',
type: 'security',
awsService: 'AWS WAF',
aliyunService: 'Web 应用防火墙',
awsOptions: [
'托管规则和自定义规则',
'速率限制和 IP 黑名单',
'与 CloudFront/ALB 集成',
'Bot Control 机器人管理'
],
aliyunOptions: [
'内置防护策略和自定义规则',
'CC 攻击防护和 IP 封禁',
'与 CDN/SLB 无缝集成',
'数据风控和爬虫管理'
],
awsPrice: '$5/月 + $0.6/百万请求',
aliyunPrice: '¥980/月起 + 流量费'
},
{
id: 'nat',
name: 'NAT 网关',
icon: '🚪',
type: 'network',
awsService: 'NAT Gateway',
aliyunService: 'NAT 网关',
awsOptions: [
'自动高可用,无需管理',
'每个 AZ 独立部署',
'支持 SNAT 出网',
'流量监控和告警'
],
aliyunOptions: [
'多可用区容灾',
'按规格选择带宽',
'SNAT/DNAT 支持',
'流量和连接数监控'
],
awsPrice: '$0.045/小时 + $0.045/GB',
aliyunPrice: '¥0.35/小时 + 流量费'
}
]
const onDragStart = (event, component) => {
draggedItem = component
event.dataTransfer.effectAllowed = 'copy'
}
const onDrop = (event) => {
event.preventDefault()
if (draggedItem) {
canvasItems.value.push({
...draggedItem,
id: `${draggedItem.id}-${Date.now()}`
})
draggedItem = null
}
}
const itemStyle = (index) => {
const positions = [
{ top: '10%', left: '10%' },
{ top: '10%', right: '10%' },
{ bottom: '10%', left: '10%' },
{ bottom: '10%', right: '10%' },
{ top: '50%', left: '50%', transform: 'translate(-50%, -50%)' }
]
return positions[index % positions.length]
}
const selectItem = (item) => {
selectedItem.value = item
}
const removeItem = (index) => {
canvasItems.value.splice(index, 1)
if (selectedItem.value && !canvasItems.value.find(i => i.id === selectedItem.value.id)) {
selectedItem.value = null
}
}
</script>
<style scoped>
.network-services-demo {
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
border-radius: 12px;
padding: 24px;
color: #fff;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
.demo-header {
text-align: center;
margin-bottom: 24px;
}
.demo-header h4 {
margin: 0 0 8px 0;
font-size: 1.25rem;
background: linear-gradient(90deg, #00d4ff, #7b2cbf);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.demo-desc {
margin: 0;
color: #8892b0;
font-size: 0.875rem;
}
.network-builder {
display: grid;
grid-template-columns: 200px 1fr;
gap: 16px;
margin-bottom: 20px;
}
.components-panel {
background: rgba(255, 255, 255, 0.03);
border-radius: 12px;
padding: 16px;
}
.panel-title {
font-weight: 600;
font-size: 0.875rem;
color: #e6f1ff;
margin-bottom: 12px;
padding-bottom: 8px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.component-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.component-item {
display: flex;
align-items: center;
gap: 8px;
padding: 10px;
background: rgba(255, 255, 255, 0.05);
border-radius: 8px;
cursor: grab;
transition: all 0.2s ease;
}
.component-item:hover {
background: rgba(255, 255, 255, 0.1);
transform: translateX(4px);
}
.component-item:active {
cursor: grabbing;
}
.component-icon {
font-size: 1.25rem;
}
.component-name {
font-size: 0.8125rem;
color: #e6f1ff;
}
.canvas-area {
background: rgba(0, 0, 0, 0.2);
border-radius: 12px;
min-height: 400px;
}
.network-canvas {
position: relative;
width: 100%;
height: 400px;
}
.empty-state {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
}
.empty-icon {
font-size: 3rem;
margin-bottom: 12px;
}
.empty-text {
font-size: 1rem;
color: #e6f1ff;
margin-bottom: 4px;
}
.empty-subtext {
font-size: 0.8125rem;
color: #8892b0;
}
.canvas-item {
position: absolute;
background: rgba(255, 255, 255, 0.08);
border: 1px solid rgba(255, 255, 255, 0.15);
border-radius: 12px;
padding: 12px 16px;
min-width: 120px;
text-align: center;
cursor: pointer;
transition: all 0.2s ease;
}
.canvas-item:hover {
background: rgba(255, 255, 255, 0.12);
transform: scale(1.05);
}
.canvas-item.network {
border-color: rgba(0, 212, 255, 0.4);
background: rgba(0, 212, 255, 0.1);
}
.canvas-item.security {
border-color: rgba(255, 99, 99, 0.4);
background: rgba(255, 99, 99, 0.1);
}
.item-icon {
font-size: 1.5rem;
margin-bottom: 4px;
}
.item-name {
font-size: 0.8125rem;
color: #e6f1ff;
font-weight: 500;
}
.remove-btn {
position: absolute;
top: -8px;
right: -8px;
width: 20px;
height: 20px;
background: #ff4444;
border: none;
border-radius: 50%;
color: #fff;
font-size: 0.875rem;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transition: opacity 0.2s;
}
.canvas-item:hover .remove-btn {
opacity: 1;
}
.config-panel {
background: rgba(0, 0, 0, 0.3);
border-radius: 12px;
padding: 20px;
animation: slideUp 0.3s ease;
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.config-header {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 16px;
padding-bottom: 12px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.config-icon {
font-size: 1.25rem;
}
.config-title {
font-weight: 600;
font-size: 1rem;
color: #e6f1ff;
flex: 1;
}
.close-config {
background: none;
border: none;
color: #8892b0;
font-size: 1.25rem;
cursor: pointer;
width: 28px;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
transition: all 0.2s;
}
.close-config:hover {
background: rgba(255, 255, 255, 0.1);
color: #fff;
}
.config-content {
display: grid;
grid-template-columns: 1fr auto 1fr;
gap: 16px;
margin-bottom: 16px;
}
.config-section {
background: rgba(255, 255, 255, 0.03);
border-radius: 10px;
padding: 16px;
}
.section-title {
font-size: 0.75rem;
color: #ff9900;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 8px;
}
.aliyun-title {
color: #ff6a00;
}
.service-name {
font-size: 1rem;
font-weight: 600;
color: #e6f1ff;
margin-bottom: 12px;
padding-bottom: 8px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.aliyun-service {
color: #e6f1ff;
}
.config-options {
display: flex;
flex-direction: column;
gap: 8px;
}
.option-item {
display: flex;
align-items: flex-start;
gap: 6px;
font-size: 0.8125rem;
color: #e6f1ff;
line-height: 1.4;
}
.option-check {
color: #ff9900;
font-weight: 700;
flex-shrink: 0;
}
.aliyun-check {
color: #ff6a00;
}
.config-divider {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.config-footer {
background: rgba(0, 0, 0, 0.2);
border-radius: 8px;
padding: 12px 16px;
}
.price-compare {
display: flex;
justify-content: space-around;
gap: 16px;
}
.price-item {
display: flex;
align-items: center;
gap: 8px;
}
.price-label {
font-size: 0.8125rem;
color: #8892b0;
}
.price-value {
font-size: 0.875rem;
color: #e6f1ff;
font-weight: 500;
}
.aliyun-price {
color: #ff6a00;
}
@media (max-width: 768px) {
.network-builder {
grid-template-columns: 1fr;
}
.components-panel {
max-height: 200px;
overflow-y: auto;
}
.config-content {
grid-template-columns: 1fr;
}
.config-divider {
display: none;
}
.price-compare {
flex-direction: column;
gap: 8px;
}
}
</style>
@@ -0,0 +1,181 @@
<template>
<div class="pricing-model-demo">
<div class="demo-header">
<h4>云服务器计费模式计算器</h4>
<p class="demo-desc">输入您的使用场景对比不同计费模式的成本</p>
</div>
<div class="calculator-inputs">
<div class="input-section">
<h5>基础配置</h5>
<div class="input-grid">
<div class="input-group">
<label>实例规格</label>
<select v-model="config.instanceType">
<option v-for="type in instanceTypes" :key="type.value" :value="type.value">
{{ type.label }}
</option>
</select>
</div>
<div class="input-group">
<label>数量 ()</label>
<input v-model.number="config.quantity" type="number" min="1" max="100" />
</div>
</div>
</div>
<div class="input-section">
<h5>使用模式</h5>
<div class="input-grid">
<div class="input-group">
<label>每日运行时长 (小时)</label>
<input v-model.number="config.dailyHours" type="range" min="1" max="24" />
<span class="range-value">{{ config.dailyHours }} 小时</span>
</div>
<div class="input-group">
<label>每月运行天数</label>
<input v-model.number="config.monthlyDays" type="range" min="1" max="31" />
<span class="range-value">{{ config.monthlyDays }} </span>
</div>
</div>
</div>
<div class="input-section">
<h5>计费偏好</h5>
<div class="billing-options">
<label v-for="option in billingOptions" :key="option.value" class="option-card"
:class="{ active: config.billingType === option.value }">
<input type="radio" v-model="config.billingType" :value="option.value" />
<span class="option-icon">{{ option.icon }}</span>
<span class="option-name">{{ option.label }}</span>
<span class="option-desc">{{ option.desc }}</span>
</label>
</div>
</div>
</div>
<div class="cost-comparison">
<h5>成本对比分析</h5>
<div class="comparison-chart">
<div v-for="model in costComparison" :key="model.type" class="chart-bar"
:class="{ recommended: model.recommended }">
<div class="bar-label">{{ model.label }}</div>
<div class="bar-visual">
<div class="bar-fill" :style="{ height: model.percentage + '%' }"
:class="model.type"></div>
</div>
<div class="bar-value">
<span class="amount">{{ model.cost }}</span>
<span v-if="model.savings" class="savings"> {{ model.savings }}</span>
</div>
<div v-if="model.recommended" class="recommend-badge">推荐</div>
</div>
</div>
</div>
<div class="recommendation-panel">
<div class="rec-header">
<span class="rec-icon">💡</span>
<span class="rec-title">优化建议</span>
</div>
<div class="rec-content">
<div v-for="(tip, index) in optimizationTips" :key="index" class="tip-item">
<span class="tip-num">{{ index + 1 }}</span>
<span class="tip-text">{{ tip }}</span>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const config = ref({
instanceType: 'medium',
quantity: 2,
dailyHours: 12,
monthlyDays: 22,
billingType: 'ondemand'
})
const instanceTypes = [
{ value: 'small', label: '小型 (2核4G)' },
{ value: 'medium', label: '中型 (4核8G)' },
{ value: 'large', label: '大型 (8核16G)' },
{ value: 'xlarge', label: '超大型 (16核32G)' }
]
const billingOptions = [
{ value: 'ondemand', label: '按需付费', icon: '⚡', desc: '按实际使用时长计费,灵活性最高' },
{ value: 'reserved', label: '预留实例', icon: '📅', desc: '预付费用换取更低单价,适合长期稳定负载' },
{ value: 'spot', label: '抢占式', icon: '💰', desc: '利用闲置资源,价格极低但可能被回收' }
]
const hourlyRates = {
small: { ondemand: 0.05, reserved: 0.03, spot: 0.015 },
medium: { ondemand: 0.10, reserved: 0.06, spot: 0.03 },
large: { ondemand: 0.20, reserved: 0.12, spot: 0.06 },
xlarge: { ondemand: 0.40, reserved: 0.24, spot: 0.12 }
}
const costComparison = computed(() => {
const rate = hourlyRates[config.value.instanceType]
const monthlyHours = config.value.dailyHours * config.value.monthlyDays * config.value.quantity
const costs = [
{ type: 'ondemand', label: '按需付费', rate: rate.ondemand },
{ type: 'reserved', label: '预留实例', rate: rate.reserved },
{ type: 'spot', label: '抢占式', rate: rate.spot }
]
const maxCost = Math.max(...costs.map(c => c.rate * monthlyHours))
return costs.map(c => {
const cost = c.rate * monthlyHours
const percentage = (cost / maxCost) * 100
const isRecommended = c.type === config.value.billingType
const savings = c.type === 'ondemand' ? null :
Math.round(((rate.ondemand - c.rate) / rate.ondemand) * 100) + '%'
return {
type: c.type,
label: c.label,
cost: '$' + cost.toFixed(2) + '/月',
percentage,
recommended: isRecommended,
savings
}
})
})
const optimizationTips = computed(() => {
const tips = []
if (config.value.dailyHours < 8) {
tips.push('每日运行时间较短,考虑使用抢占式实例降低成本')
}
if (config.value.monthlyDays > 25) {
tips.push('月度运行天数接近全月,预留实例可节省 30-60% 成本')
}
if (config.value.quantity > 5) {
tips.push('实例数量较多,建议混合使用预留实例和按需实例')
}
if (config.value.billingType === 'ondemand' && config.value.monthlyDays > 20) {
tips.push('当前使用按需付费但负载稳定,切换预留实例可显著降低成本')
}
if (tips.length === 0) {
tips.push('当前配置较为合理,建议定期监控实际使用率进行优化')
}
return tips
})
</script>
<style scoped>
/* Add styles here */
</style>
@@ -0,0 +1,511 @@
<template>
<div class="security-services-demo">
<div class="demo-header">
<h4>安全服务架构配置器</h4>
<p class="demo-desc">选择您的业务场景一键生成安全防护方案</p>
</div>
<div class="scenario-selector">
<div class="selector-title">选择业务场景</div>
<div class="scenario-cards">
<button
v-for="scenario in scenarios"
:key="scenario.id"
class="scenario-btn"
:class="{ active: selectedScenario === scenario.id }"
@click="selectScenario(scenario.id)"
>
<span class="scenario-icon">{{ scenario.icon }}</span>
<span class="scenario-name">{{ scenario.name }}</span>
</button>
</div>
</div>
<div v-if="selectedScenarioData" class="security-architecture">
<div class="architecture-header">
<span class="header-icon">🏗</span>
<span class="header-title">推荐安全架构</span>
</div>
<div class="architecture-layers">
<div class="layer edge-layer">
<div class="layer-title">
<span class="layer-icon">🌐</span>
边缘防护层
</div>
<div class="layer-services">
<div class="service-card">
<div class="service-header aws">
<span class="service-name">{{ selectedScenarioData.edge.aws }}</span>
</div>
<div class="service-features">
<div v-for="(feat, idx) in selectedScenarioData.edge.awsFeatures" :key="idx" class="feature">
{{ feat }}
</div>
</div>
</div>
<div class="vs-mini">VS</div>
<div class="service-card">
<div class="service-header aliyun">
<span class="service-name">{{ selectedScenarioData.edge.aliyun }}</span>
</div>
<div class="service-features">
<div v-for="(feat, idx) in selectedScenarioData.edge.aliyunFeatures" :key="idx" class="feature">
{{ feat }}
</div>
</div>
</div>
</div>
</div>
<div class="layer application-layer">
<div class="layer-title">
<span class="layer-icon">🔐</span>
应用安全层
</div>
<div class="layer-services">
<div class="service-card">
<div class="service-header aws">
<span class="service-name">{{ selectedScenarioData.app.aws }}</span>
</div>
<div class="service-features">
<div v-for="(feat, idx) in selectedScenarioData.app.awsFeatures" :key="idx" class="feature">
{{ feat }}
</div>
</div>
</div>
<div class="vs-mini">VS</div>
<div class="service-card">
<div class="service-header aliyun">
<span class="service-name">{{ selectedScenarioData.app.aliyun }}</span>
</div>
<div class="service-features">
<div v-for="(feat, idx) in selectedScenarioData.app.aliyunFeatures" :key="idx" class="feature">
{{ feat }}
</div>
</div>
</div>
</div>
</div>
<div class="layer data-layer">
<div class="layer-title">
<span class="layer-icon">🗝</span>
数据安全层
</div>
<div class="layer-services">
<div class="service-card">
<div class="service-header aws">
<span class="service-name">{{ selectedScenarioData.data.aws }}</span>
</div>
<div class="service-features">
<div v-for="(feat, idx) in selectedScenarioData.data.awsFeatures" :key="idx" class="feature">
{{ feat }}
</div>
</div>
</div>
<div class="vs-mini">VS</div>
<div class="service-card">
<div class="service-header aliyun">
<span class="service-name">{{ selectedScenarioData.data.aliyun }}</span>
</div>
<div class="service-features">
<div v-for="(feat, idx) in selectedScenarioData.data.aliyunFeatures" :key="idx" class="feature">
{{ feat }}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="security-recommendations">
<div class="rec-title">💡 安全建议</div>
<div class="rec-list">
<div v-for="(rec, idx) in selectedScenarioData.recommendations" :key="idx" class="rec-item">
<span class="rec-num">{{ idx + 1 }}</span>
<span class="rec-text">{{ rec }}</span>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const selectedScenario = ref('web')
const scenarios = [
{ id: 'web', name: 'Web 应用', icon: '🌐' },
{ id: 'api', name: 'API 服务', icon: '🔌' },
{ id: 'mobile', name: '移动应用', icon: '📱' },
{ id: 'enterprise', name: '企业系统', icon: '🏢' }
]
const scenarioData = {
web: {
edge: {
aws: 'CloudFront + WAF',
aliyun: 'CDN + WAF',
awsFeatures: ['全球 400+ 边缘节点', 'DDoS 防护和 Bot 管理', '自动 SSL/TLS 加密'],
aliyunFeatures: ['国内 2800+ 节点', 'CC 攻击防护和防爬虫', 'HTTPS 证书一键部署']
},
app: {
aws: 'AWS WAF + Shield',
aliyun: 'Web 应用防火墙',
awsFeatures: ['SQL 注入和 XSS 防护', '速率限制和 IP 黑名单', '托管规则和自定义规则'],
aliyunFeatures: ['OWASP Top 10 防护', '敏感数据防泄漏', '智能 CC 防护策略']
},
data: {
aws: 'KMS + Secrets Manager',
aliyun: 'KMS + 凭据管家',
awsFeatures: ['AES-256 加密算法', '自动密钥轮换', '与 AWS 服务原生集成'],
aliyunFeatures: ['国密算法支持', '密钥版本管理', 'RAM 细粒度权限控制']
},
recommendations: [
'启用 HTTPS 强制跳转,配置 HSTS 头部',
'设置 WAF 规则防御 SQL 注入、XSS 等常见攻击',
'启用 CDN 缓存静态资源,减少源站压力',
'配置敏感数据加密存储,使用 KMS 管理密钥'
]
},
api: {
edge: {
aws: 'API Gateway + WAF',
aliyun: 'API 网关 + WAF',
awsFeatures: ['API 版本管理和流量控制', '缓存和节流策略', '请求/响应转换'],
aliyunFeatures: ['API 发布和生命周期管理', '流量控制和访问频次限制', '参数校验和Mock 数据']
},
app: {
aws: 'Cognito + IAM',
aliyun: '应用身份服务 + RAM',
awsFeatures: ['OAuth 2.0 和 OpenID Connect', '用户池和身份池', 'MFA 多因素认证'],
aliyunFeatures: ['OIDC 和 SAML 协议支持', '企业 AD/LDAP 集成', '实人认证和设备指纹']
},
data: {
aws: 'KMS + Parameter Store',
aliyun: 'KMS + 应用配置管理',
awsFeatures: ['API 密钥加密存储', '配置参数版本管理', '与 CloudFormation 集成'],
aliyunFeatures: ['敏感配置加密', '配置灰度发布', '配置变更审计']
},
recommendations: [
'实施 API 认证鉴权,使用 OAuth 2.0 或 API Key',
'配置 API 网关的速率限制,防止暴力破解',
'对敏感 API 实施 IP 白名单限制',
'加密存储 API 密钥和敏感配置'
]
},
mobile: {
edge: {
aws: 'CloudFront + WAF',
aliyun: 'CDN + WAF',
awsFeatures: ['移动网络优化', 'HTTP/2 和 QUIC 支持', '智能压缩和图像优化'],
aliyunFeatures: ['移动加速方案', '弱网环境优化', '自适应码率调整']
},
app: {
aws: 'Cognito + Device Farm',
aliyun: '应用身份服务 + 移动测试',
awsFeatures: ['设备指纹识别', '设备风险评估', '越狱/Root 检测'],
aliyunFeatures: ['设备可信认证', '作弊设备识别', '安全键盘输入']
},
data: {
aws: 'KMS + S3',
aliyun: 'KMS + OSS',
awsFeatures: ['移动端数据加密', '本地缓存加密', '密钥安全存储'],
aliyunFeatures: ['国密 SM4 支持', '本地数据库加密', '密钥白盒保护']
},
recommendations: [
'实施设备绑定和设备指纹识别',
'检测越狱/Root 设备并限制访问',
'本地敏感数据加密存储',
'使用 HTTPS 证书绑定防止中间人攻击'
]
},
enterprise: {
edge: {
aws: 'CloudFront + WAF + Shield Advanced',
aliyun: 'CDN + WAF + DDoS 高防',
awsFeatures: ['DDoS 攻击自动缓解', '24/7 DRT 团队支持', '成本保护保障'],
aliyunFeatures: ['T 级 DDoS 防护能力', 'CC 攻击智能清洗', '专家应急响应']
},
app: {
aws: 'IAM + SSO + Directory Service',
aliyun: 'RAM + IDaaS + 云 SSO',
awsFeatures: ['与企业 AD 集成', '单点登录 SSO', '临时凭证和权限边界'],
aliyunFeatures: ['LDAP/AD 目录同步', 'SaaS 应用集成', '细粒度权限管控']
},
data: {
aws: 'KMS + CloudHSM + Macie',
aliyun: 'KMS + 加密服务 + 敏感数据保护',
awsFeatures: ['FIPS 140-2 Level 3 HSM', '自动敏感数据发现', '密钥分级管理'],
aliyunFeatures: ['国密局认证 HSM', '敏感数据自动识别', '合规审计报告']
},
recommendations: [
'部署 DDoS 高防和 WAF 多层防护',
'实施统一身份管理和 SSO 单点登录',
'启用数据加密和敏感数据保护',
'建立安全审计和合规监控体系'
]
}
}
const selectScenario = (id) => {
selectedScenario.value = id
}
const currentScenario = computed(() => scenarioData[selectedScenario.value])
</script>
<style scoped>
.network-services-demo {
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
border-radius: 12px;
padding: 24px;
color: #fff;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
.demo-header {
text-align: center;
margin-bottom: 24px;
}
.demo-header h4 {
margin: 0 0 8px 0;
font-size: 1.25rem;
background: linear-gradient(90deg, #00d4ff, #7b2cbf);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.demo-desc {
margin: 0;
color: #8892b0;
font-size: 0.875rem;
}
.scenario-selector {
margin-bottom: 20px;
}
.selector-title {
font-size: 0.9375rem;
font-weight: 500;
color: #e6f1ff;
margin-bottom: 12px;
}
.scenario-cards {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.scenario-btn {
display: flex;
align-items: center;
gap: 6px;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
color: #e6f1ff;
padding: 8px 16px;
border-radius: 20px;
cursor: pointer;
font-size: 0.875rem;
transition: all 0.2s ease;
}
.scenario-btn:hover {
background: rgba(255, 255, 255, 0.1);
}
.scenario-btn.active {
background: linear-gradient(135deg, #00d4ff, #7b2cbf);
border-color: transparent;
color: #fff;
}
.scenario-icon {
font-size: 1rem;
}
.security-architecture {
background: rgba(0, 0, 0, 0.2);
border-radius: 12px;
padding: 20px;
}
.architecture-header {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 16px;
padding-bottom: 12px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.header-icon {
font-size: 1.25rem;
}
.header-title {
font-weight: 600;
font-size: 1rem;
color: #e6f1ff;
}
.architecture-layers {
display: flex;
flex-direction: column;
gap: 16px;
}
.layer {
background: rgba(255, 255, 255, 0.03);
border-radius: 10px;
padding: 16px;
}
.layer-title {
display: flex;
align-items: center;
gap: 8px;
font-weight: 600;
font-size: 0.9375rem;
color: #e6f1ff;
margin-bottom: 12px;
}
.layer-icon {
font-size: 1.25rem;
}
.layer-services {
display: grid;
grid-template-columns: 1fr auto 1fr;
gap: 12px;
align-items: start;
}
.service-card {
background: rgba(0, 0, 0, 0.2);
border-radius: 8px;
overflow: hidden;
}
.service-header {
padding: 10px 12px;
font-weight: 600;
font-size: 0.875rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.service-header.aws {
background: rgba(255, 153, 0, 0.2);
color: #ff9900;
}
.service-header.aliyun {
background: rgba(255, 106, 0, 0.2);
color: #ff6a00;
}
.service-features {
padding: 12px;
}
.feature {
font-size: 0.8125rem;
color: #e6f1ff;
padding: 4px 0;
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}
.feature:last-child {
border-bottom: none;
}
.vs-mini {
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #00d4ff, #7b2cbf);
color: #fff;
width: 28px;
height: 28px;
border-radius: 50%;
font-size: 0.625rem;
font-weight: 700;
align-self: center;
}
.security-recommendations {
margin-top: 16px;
padding-top: 16px;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.rec-title {
font-weight: 600;
font-size: 1rem;
color: #00d4ff;
margin-bottom: 12px;
}
.rec-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.rec-item {
display: flex;
align-items: flex-start;
gap: 10px;
background: rgba(255, 255, 255, 0.03);
padding: 10px 12px;
border-radius: 8px;
border-left: 3px solid #00d4ff;
}
.rec-num {
background: linear-gradient(135deg, #00d4ff, #7b2cbf);
color: #fff;
width: 22px;
height: 22px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.75rem;
font-weight: 700;
flex-shrink: 0;
}
.rec-text {
font-size: 0.875rem;
color: #e6f1ff;
line-height: 1.5;
}
@media (max-width: 768px) {
.scenario-cards {
grid-template-columns: repeat(2, 1fr);
}
.layer-services {
grid-template-columns: 1fr;
}
.vs-mini {
display: none;
}
.config-content {
grid-template-columns: 1fr;
}
}
</style>
@@ -0,0 +1,214 @@
<template>
<div class="service-selection-demo">
<div class="demo-header">
<h4>云服务选型决策树</h4>
<p class="demo-desc">回答几个简单问题获取最适合您的云服务方案</p>
</div>
<div v-if="!result" class="decision-flow">
<div class="progress-bar">
<div class="progress-fill" :style="{ width: progress + '%' }"></div>
</div>
<div class="question-card">
<div class="question-number">问题 {{ currentStep + 1 }}/{{ questions.length }}</div>
<h5 class="question-text">{{ currentQuestion.text }}</h5>
<div class="options-list">
<button
v-for="option in currentQuestion.options"
:key="option.value"
class="option-btn"
@click="selectOption(option)"
>
<span class="option-icon">{{ option.icon }}</span>
<span class="option-text">{{ option.text }}</span>
<span class="option-desc">{{ option.desc }}</span>
</button>
</div>
</div>
</div>
<div v-else class="result-panel">
<div class="result-header">
<span class="result-icon">🎯</span>
<h5>推荐方案</h5>
</div>
<div class="recommendation-cards">
<div class="rec-card primary">
<div class="rec-badge">最佳匹配</div>
<div class="rec-icon">{{ result.primary.icon }}</div>
<div class="rec-title">{{ result.primary.name }}</div>
<div class="rec-services">
<span class="service aws">{{ result.primary.aws }}</span>
<span class="vs">vs</span>
<span class="service aliyun">{{ result.primary.aliyun }}</span>
</div>
<div class="rec-reason">{{ result.primary.reason }}</div>
</div>
<div class="rec-card secondary">
<div class="rec-badge alt">备选</div>
<div class="rec-icon">{{ result.secondary.icon }}</div>
<div class="rec-title">{{ result.secondary.name }}</div>
<div class="rec-services">
<span class="service aws">{{ result.secondary.aws }}</span>
<span class="vs">vs</span>
<span class="service aliyun">{{ result.secondary.aliyun }}</span>
</div>
<div class="rec-reason">{{ result.secondary.reason }}</div>
</div>
</div>
<div class="result-actions">
<button class="restart-btn" @click="restart">
<span></span> 重新测试
</button>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const currentStep = ref(0)
const answers = ref([])
const questions = [
{
text: '您的应用主要面向哪个地区?',
options: [
{ value: 'global', icon: '🌍', text: '全球用户', desc: '需要覆盖多个国家和地区' },
{ value: 'china', icon: '🇨🇳', text: '中国大陆', desc: '主要服务国内用户' },
{ value: 'asia', icon: '🌏', text: '亚太区域', desc: '覆盖亚洲及太平洋地区' },
{ value: 'us', icon: '🇺🇸', text: '北美/欧洲', desc: '主要服务欧美用户' }
]
},
{
text: '您的应用对计算资源的需求如何?',
options: [
{ value: 'serverless', icon: '⚡', text: '事件驱动/无服务器', desc: '按需运行,流量波动大' },
{ value: 'webapp', icon: '🌐', text: 'Web 应用服务', desc: '需要 24/7 在线运行' },
{ value: 'batch', icon: '📊', text: '批处理/计算任务', desc: '定时或按需批量执行' },
{ value: 'hpc', icon: '🔬', text: '高性能计算', desc: '需要 GPU 或大规模集群' }
]
},
{
text: '您对成本优化的优先级是?',
options: [
{ value: 'lowest', icon: '💰', text: '极致成本优化', desc: '可以接受复杂配置换取最低价' },
{ value: 'balanced', icon: '⚖️', text: '平衡型', desc: '在成本和易用性间找平衡' },
{ value: 'stable', icon: '📈', text: '成本可预测', desc: '偏好固定成本,方便预算' },
{ value: 'premium', icon: '💎', text: '性能优先', desc: '成本次之,追求最佳性能' }
]
},
{
text: '您的数据存储需求主要是?',
options: [
{ value: 'object', icon: '📦', text: '对象存储(文件/图片/视频)', desc: '海量非结构化数据' },
{ value: 'database', icon: '🗄️', text: '数据库存储', desc: '结构化数据和事务处理' },
{ value: 'cache', icon: '⚡', text: '缓存/会话存储', desc: '高性能临时数据存储' },
{ value: 'mixed', icon: '🔀', text: '混合存储', desc: '多种存储类型组合' }
]
}
]
const progress = computed(() => {
return ((currentStep.value + 1) / questions.length) * 100
})
const currentQuestion = computed(() => {
return questions[currentStep.value]
})
const selectOption = (option) => {
answers.value.push(option.value)
if (currentStep.value < questions.length - 1) {
currentStep.value++
}
}
const result = computed(() => {
if (answers.value.length < 4) return null
const [region, compute, cost, storage] = answers.value
// 计算推荐
let primary, secondary
if (compute === 'serverless') {
primary = {
icon: '⚡',
name: '无服务器架构',
aws: 'AWS Lambda + API Gateway',
aliyun: '函数计算 + API 网关',
reason: '事件驱动场景下,按调用计费,无需预置服务器资源'
}
secondary = {
icon: '🔲',
name: '容器服务',
aws: 'AWS Fargate',
aliyun: 'Serverless Kubernetes',
reason: '需要长时间运行但需要灵活扩缩容的场景'
}
} else if (compute === 'hpc') {
primary = {
icon: '🔬',
name: '高性能计算集群',
aws: 'AWS ParallelCluster',
aliyun: 'E-HPC + 超级计算集群',
reason: 'GPU 实例和高速互联网络,满足科学计算和 AI 训练需求'
}
secondary = {
icon: '⚡',
name: '弹性裸金属',
aws: 'EC2 Bare Metal',
aliyun: '弹性裸金属服务器',
reason: '需要物理机性能但希望云化管理的场景'
}
} else if (cost === 'lowest') {
primary = {
icon: '💰',
name: '抢占式实例',
aws: 'EC2 Spot Instances',
aliyun: '抢占式实例',
reason: '价格最低至按需实例的 10%,适合容错性高的批处理任务'
}
secondary = {
icon: '📅',
name: '预留实例',
aws: 'Reserved Instances',
aliyun: '包年包月',
reason: '长期稳定负载选择预留实例,可节省 30-60% 成本'
}
} else {
primary = {
icon: '☁️',
name: '云服务器 ECS',
aws: 'Amazon EC2',
aliyun: 'ECS 云服务器',
reason: '最通用的计算服务,支持多种计费模式和实例规格,生态完善'
}
secondary = {
icon: '📦',
name: '容器实例',
aws: 'AWS Fargate',
aliyun: 'ECI 容器实例',
reason: '无需管理服务器,直接运行容器,适合微服务架构'
}
}
return { primary, secondary }
})
const restart = () => {
currentStep.value = 0
answers.value = []
}
</script>
<style scoped>
/* Add styles here */
</style>
@@ -0,0 +1,489 @@
<template>
<div class="storage-services-demo">
<div class="demo-header">
<h4>存储服务选型助手</h4>
<p class="demo-desc">根据您的使用场景推荐最适合的存储方案</p>
</div>
<div class="scenario-selector">
<div class="selector-title">选择您的主要使用场景</div>
<div class="scenario-grid">
<button
v-for="scenario in scenarios"
:key="scenario.id"
class="scenario-card"
:class="{ active: selectedScenario === scenario.id }"
@click="selectScenario(scenario.id)"
>
<div class="scenario-icon">{{ scenario.icon }}</div>
<div class="scenario-name">{{ scenario.name }}</div>
<div class="scenario-desc">{{ scenario.shortDesc }}</div>
</button>
</div>
</div>
<div v-if="selectedScenario" class="recommendation-result">
<div class="result-header">
<span class="result-icon">🎯</span>
<span class="result-title">推荐方案</span>
</div>
<div class="storage-comparison">
<div class="provider-card aws">
<div class="provider-header">
<div class="provider-logo">AWS</div>
<div class="provider-service">{{ currentScenario.awsService }}</div>
</div>
<div class="provider-features">
<div v-for="(feature, idx) in currentScenario.awsFeatures" :key="idx" class="feature-item">
<span class="check"></span>
<span>{{ feature }}</span>
</div>
</div>
<div class="provider-pricing">
<div class="price-label">定价模式</div>
<div class="price-value">{{ currentScenario.awsPricing }}</div>
</div>
</div>
<div class="vs-divider">
<div class="vs-line"></div>
<div class="vs-badge">VS</div>
<div class="vs-line"></div>
</div>
<div class="provider-card aliyun">
<div class="provider-header">
<div class="provider-logo aliyun-logo">阿里云</div>
<div class="provider-service">{{ currentScenario.aliyunService }}</div>
</div>
<div class="provider-features">
<div v-for="(feature, idx) in currentScenario.aliyunFeatures" :key="idx" class="feature-item">
<span class="check aliyun-check"></span>
<span>{{ feature }}</span>
</div>
</div>
<div class="provider-pricing">
<div class="price-label">定价模式</div>
<div class="price-value">{{ currentScenario.aliyunPricing }}</div>
</div>
</div>
</div>
<div class="decision-guide">
<div class="guide-title">🤔 如何选择</div>
<div class="guide-content">
<div class="guide-item">
<div class="guide-condition">选择 AWS 如果</div>
<div class="guide-reason">{{ currentScenario.chooseAwsWhen }}</div>
</div>
<div class="guide-item">
<div class="guide-condition">选择阿里云如果</div>
<div class="guide-reason">{{ currentScenario.chooseAliyunWhen }}</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const selectedScenario = ref(null)
const scenarios = [
{
id: 'website',
name: '静态网站托管',
icon: '🌐',
shortDesc: '托管 HTML/CSS/JS 等静态资源',
awsService: 'Amazon S3 + CloudFront',
aliyunService: 'OSS + CDN',
awsFeatures: [
'全球 400+ 边缘节点加速',
'自动压缩和 HTTP/2 支持',
'与 Route 53 无缝集成',
'支持静态网站托管配置'
],
aliyunFeatures: [
'国内 2800+ 节点覆盖',
'智能压缩和 QUIC 协议支持',
'与万网域名一键绑定',
'实时日志分析和监控'
],
awsPricing: '存储 $0.023/GB/月 + 流量 $0.085-0.12/GB',
aliyunPricing: '存储 ¥0.12/GB/月 + 流量 ¥0.24-0.80/GB',
chooseAwsWhen: '用户主要在海外,需要全球加速,或已使用 AWS 其他服务',
chooseAliyunWhen: '用户主要在中国大陆,需要备案支持,追求国内访问速度'
},
{
id: 'database',
name: '数据库存储',
icon: '🗄️',
shortDesc: '关系型和非关系型数据库',
awsService: 'Amazon RDS/Aurora',
aliyunService: 'RDS/PolarDB',
awsFeatures: [
'Aurora 性能是 MySQL 的 5 倍',
'自动故障转移和读副本',
'支持 6 种数据库引擎',
'Serverless 自动扩缩容'
],
aliyunFeatures: [
'PolarDB 计算存储分离架构',
'一写多读,读写分离',
'秒级备份和恢复',
'Oracle 语法兼容模式'
],
awsPricing: '按需 $0.017-0.68/小时,预留可省 40-60%',
aliyunPricing: '按量 ¥0.12-4.8/小时,包年包月更优惠',
chooseAwsWhen: '需要 Aurora 的高性能,或有多种数据库引擎需求',
chooseAliyunWhen: '需要 Oracle 兼容,或追求性价比和本地化支持'
},
{
id: 'backup',
name: '备份与归档',
icon: '💾',
shortDesc: '冷数据和长期归档存储',
awsService: 'Amazon S3 Glacier',
aliyunService: 'OSS 归档存储',
awsFeatures: [
'Glacier Deep Archive cheapest',
'检索时间从分钟到小时可选',
'S3 生命周期策略自动迁移',
'WORM 合规保留策略'
],
aliyunFeatures: [
'归档存储单价行业最低',
'解冻时间可配置',
'跨地域冗余存储',
'符合国内合规要求'
],
awsPricing: 'Glacier $0.004/GB/月,Deep Archive $0.00099/GB/月',
aliyunPricing: '归档存储 ¥0.033/GB/月,冷归档更低',
chooseAwsWhen: '需要 Deep Archive 超低成本,或有复杂生命周期策略',
chooseAliyunWhen: '数据需在国内归档,或追求极致性价比'
},
{
id: 'media',
name: '媒体处理',
icon: '🎬',
shortDesc: '音视频存储和分发',
awsService: 'S3 + Elemental',
aliyunService: 'OSS + 媒体处理',
awsFeatures: [
'Elemental 专业级视频处理',
'MediaConvert 格式转码',
'MediaLive 直播流处理',
'CloudFront 低延迟分发'
],
aliyunFeatures: [
'视频截帧、转码、水印',
'智能封面和内容审核',
'直播录制和时移回看',
'CDN 全球加速分发'
],
awsPricing: '按使用量计费,转码 $0.007-0.1/分钟',
aliyunPricing: '按量计费,转码 ¥0.03-0.5/分钟',
chooseAwsWhen: '需要广播级专业处理,或全球直播分发',
chooseAliyunWhen: '需要智能内容审核,或国内视频处理'
}
]
const selectScenario = (id) => {
selectedScenario.value = id
}
const currentScenario = computed(() => {
return scenarios.find(s => s.id === selectedScenario.value)
})
</script>
<style scoped>
.storage-services-demo {
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
border-radius: 12px;
padding: 24px;
color: #fff;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
.demo-header {
text-align: center;
margin-bottom: 24px;
}
.demo-header h4 {
margin: 0 0 8px 0;
font-size: 1.25rem;
background: linear-gradient(90deg, #00d4ff, #7b2cbf);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.demo-desc {
margin: 0;
color: #8892b0;
font-size: 0.875rem;
}
.scenario-selector {
margin-bottom: 24px;
}
.selector-title {
font-size: 0.9375rem;
font-weight: 500;
color: #e6f1ff;
margin-bottom: 12px;
}
.scenario-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 12px;
}
.scenario-card {
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 10px;
padding: 16px;
cursor: pointer;
text-align: center;
transition: all 0.3s ease;
}
.scenario-card:hover {
background: rgba(255, 255, 255, 0.06);
transform: translateY(-2px);
}
.scenario-card.active {
background: linear-gradient(135deg, rgba(0, 212, 255, 0.15), rgba(123, 44, 191, 0.15));
border-color: rgba(0, 212, 255, 0.3);
}
.scenario-icon {
font-size: 2rem;
margin-bottom: 8px;
}
.scenario-name {
font-weight: 600;
font-size: 0.9375rem;
color: #e6f1ff;
margin-bottom: 4px;
}
.scenario-desc {
font-size: 0.75rem;
color: #8892b0;
}
.recommendation-result {
animation: slideUp 0.4s ease;
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.result-header {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 16px;
padding-bottom: 12px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.result-icon {
font-size: 1.25rem;
}
.result-title {
font-weight: 600;
font-size: 1rem;
color: #e6f1ff;
}
.storage-comparison {
display: grid;
grid-template-columns: 1fr auto 1fr;
gap: 12px;
margin-bottom: 20px;
}
.provider-card {
background: rgba(255, 255, 255, 0.03);
border-radius: 12px;
padding: 16px;
border: 1px solid rgba(255, 255, 255, 0.08);
}
.provider-card.aws {
border-top: 3px solid #ff9900;
}
.provider-card.aliyun {
border-top: 3px solid #ff6a00;
}
.provider-header {
text-align: center;
margin-bottom: 12px;
padding-bottom: 12px;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.provider-logo {
font-size: 1.25rem;
font-weight: 700;
color: #ff9900;
}
.provider-logo.aliyun-logo {
color: #ff6a00;
}
.provider-service {
font-size: 0.8125rem;
color: #8892b0;
margin-top: 4px;
}
.provider-features {
display: flex;
flex-direction: column;
gap: 8px;
margin-bottom: 12px;
}
.feature-item {
display: flex;
align-items: flex-start;
gap: 6px;
font-size: 0.8125rem;
color: #e6f1ff;
line-height: 1.4;
}
.check {
color: #ff9900;
font-weight: 700;
flex-shrink: 0;
}
.aliyun-check {
color: #ff6a00;
}
.provider-pricing {
background: rgba(0, 0, 0, 0.2);
border-radius: 8px;
padding: 10px;
}
.price-label {
font-size: 0.75rem;
color: #8892b0;
margin-bottom: 4px;
}
.price-value {
font-size: 0.8125rem;
color: #e6f1ff;
font-weight: 500;
}
.vs-divider {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 8px;
}
.vs-line {
width: 1px;
flex: 1;
background: rgba(255, 255, 255, 0.1);
}
.vs-badge {
background: linear-gradient(135deg, #00d4ff, #7b2cbf);
color: #fff;
width: 32px;
height: 32px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
font-size: 0.75rem;
}
.decision-guide {
background: rgba(0, 0, 0, 0.2);
border-radius: 12px;
padding: 16px;
}
.guide-title {
font-weight: 600;
font-size: 1rem;
color: #e6f1ff;
margin-bottom: 12px;
}
.guide-content {
display: flex;
flex-direction: column;
gap: 12px;
}
.guide-item {
padding: 12px;
background: rgba(255, 255, 255, 0.03);
border-radius: 8px;
border-left: 3px solid #00d4ff;
}
.guide-condition {
font-size: 0.8125rem;
color: #00d4ff;
font-weight: 500;
margin-bottom: 4px;
}
.guide-reason {
font-size: 0.875rem;
color: #e6f1ff;
line-height: 1.5;
}
@media (max-width: 768px) {
.scenario-grid {
grid-template-columns: 1fr;
}
.storage-comparison {
grid-template-columns: 1fr;
gap: 16px;
}
.vs-divider {
display: none;
}
}
</style>