3af119a598
- 新增 Vibe Coding 全栈相关演示组件 (DeveloperSkillShift, FrontendTriad, BackendCore 等) - 新增 RAG 相关组件 (RAGPipeline, ChunkingStrategy, Retrieval 等) - 新增 Embedding & Vector 相关组件 (EmbeddingConcept, VectorSimilarity 等) - 新增 AI Native App 设计组件 (AINativeArch, PromptDesign 等) - 新增 Infrastructure as Code 组件 (IaCConcept, TerraformWorkflow 等) - 新增 DNS & HTTPS 演示组件 (DnsResolution, HttpsHandshake 等) - 新增 Model Finetuning 组件 (FinetuningPipeline 等) - 更新多个章节的 markdown 内容,集成交互式演示
375 lines
10 KiB
Vue
375 lines
10 KiB
Vue
<template>
|
||
<div class="iac-tool-comparison-demo">
|
||
<div class="demo-label">交互演示 ── 主流 IaC 工具对比</div>
|
||
|
||
<div class="tool-selector">
|
||
<span class="selector-hint">选择要对比的工具(至少选 2 个):</span>
|
||
<div class="tool-chips">
|
||
<button
|
||
v-for="tool in tools"
|
||
:key="tool.name"
|
||
:class="['tool-chip', { selected: selectedTools.includes(tool.name) }]"
|
||
:style="selectedTools.includes(tool.name) ? { background: tool.color, borderColor: tool.color, color: '#fff' } : {}"
|
||
@click="toggleTool(tool.name)"
|
||
>
|
||
{{ tool.icon }} {{ tool.name }}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div v-if="selectedTools.length >= 2" class="comparison-grid">
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th class="feature-col">特性</th>
|
||
<th v-for="name in selectedTools" :key="name" class="tool-col">
|
||
<span class="tool-header-icon">{{ getToolByName(name).icon }}</span>
|
||
<span>{{ name }}</span>
|
||
</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr v-for="feature in features" :key="feature.key">
|
||
<td class="feature-cell">{{ feature.label }}</td>
|
||
<td v-for="name in selectedTools" :key="name" class="value-cell">
|
||
<span :class="getCellClass(name, feature.key)">
|
||
{{ getToolByName(name).features[feature.key] }}
|
||
</span>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<div v-else class="empty-hint">
|
||
请至少选择 2 个工具进行对比
|
||
</div>
|
||
|
||
<Transition name="fade">
|
||
<div v-if="selectedDetail" class="detail-card">
|
||
<div class="detail-header">
|
||
<span class="detail-icon">{{ selectedDetail.icon }}</span>
|
||
<span class="detail-name">{{ selectedDetail.name }}</span>
|
||
<button class="close-btn" @click="detailName = ''">✕</button>
|
||
</div>
|
||
<p class="detail-desc">{{ selectedDetail.desc }}</p>
|
||
<div class="detail-code">
|
||
<div class="code-label">示例代码片段:</div>
|
||
<pre class="code-block"><code>{{ selectedDetail.example }}</code></pre>
|
||
</div>
|
||
</div>
|
||
</Transition>
|
||
|
||
<div class="detail-hint" v-if="selectedTools.length >= 2 && !detailName">
|
||
点击下方工具名称查看详细介绍和代码示例
|
||
</div>
|
||
<div class="tool-detail-btns" v-if="selectedTools.length >= 2">
|
||
<button
|
||
v-for="name in selectedTools"
|
||
:key="name"
|
||
:class="['detail-btn', { active: detailName === name }]"
|
||
@click="detailName = detailName === name ? '' : name"
|
||
>
|
||
{{ getToolByName(name).icon }} {{ name }}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, computed } from 'vue'
|
||
|
||
const selectedTools = ref(['Terraform', 'CloudFormation'])
|
||
const detailName = ref('')
|
||
|
||
const selectedDetail = computed(() => {
|
||
if (!detailName.value) return null
|
||
return tools.find(t => t.name === detailName.value)
|
||
})
|
||
|
||
const features = [
|
||
{ key: 'vendor', label: '厂商' },
|
||
{ key: 'language', label: '配置语言' },
|
||
{ key: 'style', label: '声明式/命令式' },
|
||
{ key: 'multiCloud', label: '多云支持' },
|
||
{ key: 'stateManagement', label: '状态管理' },
|
||
{ key: 'learning', label: '学习曲线' },
|
||
{ key: 'community', label: '社区生态' },
|
||
{ key: 'bestFor', label: '最佳场景' }
|
||
]
|
||
|
||
const tools = [
|
||
{
|
||
name: 'Terraform',
|
||
icon: '🟣',
|
||
color: '#7c3aed',
|
||
features: {
|
||
vendor: 'HashiCorp',
|
||
language: 'HCL',
|
||
style: '声明式',
|
||
multiCloud: '原生多云',
|
||
stateManagement: 'State 文件',
|
||
learning: '中等',
|
||
community: '非常活跃',
|
||
bestFor: '多云/混合云'
|
||
},
|
||
desc: 'Terraform 是目前最流行的开源 IaC 工具,由 HashiCorp 开发。它使用自研的 HCL 语言,通过 Provider 机制支持几乎所有主流云平台。',
|
||
example: `resource "aws_s3_bucket" "data" {
|
||
bucket = "my-data-bucket"
|
||
tags = { Env = "prod" }
|
||
}
|
||
|
||
resource "aws_instance" "web" {
|
||
ami = "ami-0c55b159"
|
||
instance_type = "t3.micro"
|
||
}`
|
||
},
|
||
{
|
||
name: 'CloudFormation',
|
||
icon: '🟠',
|
||
color: '#ea580c',
|
||
features: {
|
||
vendor: 'AWS',
|
||
language: 'YAML / JSON',
|
||
style: '声明式',
|
||
multiCloud: '仅 AWS',
|
||
stateManagement: 'AWS 托管',
|
||
learning: '中等偏高',
|
||
community: 'AWS 生态',
|
||
bestFor: '纯 AWS 环境'
|
||
},
|
||
desc: 'CloudFormation 是 AWS 原生的 IaC 服务,与 AWS 服务深度集成。状态由 AWS 自动管理,无需额外维护 State 文件。',
|
||
example: `Resources:
|
||
WebServer:
|
||
Type: AWS::EC2::Instance
|
||
Properties:
|
||
ImageId: ami-0c55b159
|
||
InstanceType: t3.micro
|
||
Tags:
|
||
- Key: Name
|
||
Value: web-server`
|
||
},
|
||
{
|
||
name: 'Pulumi',
|
||
icon: '🔵',
|
||
color: '#2563eb',
|
||
features: {
|
||
vendor: 'Pulumi',
|
||
language: 'TypeScript/Python/Go',
|
||
style: '命令式 + 声明式',
|
||
multiCloud: '原生多云',
|
||
stateManagement: 'Pulumi Cloud / 自管',
|
||
learning: '低(熟悉编程语言)',
|
||
community: '快速增长',
|
||
bestFor: '开发者友好场景'
|
||
},
|
||
desc: 'Pulumi 允许使用真正的编程语言(TypeScript、Python、Go 等)来定义基础设施,对开发者非常友好,支持条件判断、循环等编程特性。',
|
||
example: `import * as aws from "@pulumi/aws"
|
||
|
||
const bucket = new aws.s3.Bucket("data", {
|
||
tags: { Env: "prod" }
|
||
})
|
||
|
||
const server = new aws.ec2.Instance("web", {
|
||
ami: "ami-0c55b159",
|
||
instanceType: "t3.micro",
|
||
})`
|
||
},
|
||
{
|
||
name: 'Ansible',
|
||
icon: '🔴',
|
||
color: '#dc2626',
|
||
features: {
|
||
vendor: 'Red Hat',
|
||
language: 'YAML (Playbook)',
|
||
style: '命令式',
|
||
multiCloud: '通过模块支持',
|
||
stateManagement: '无状态(幂等)',
|
||
learning: '低',
|
||
community: '非常活跃',
|
||
bestFor: '配置管理 + 编排'
|
||
},
|
||
desc: 'Ansible 是一个无代理的自动化工具,擅长配置管理和应用部署。它通过 SSH 连接目标机器执行任务,无需安装客户端。',
|
||
example: `- name: 部署 Web 服务器
|
||
hosts: webservers
|
||
tasks:
|
||
- name: 安装 Nginx
|
||
apt:
|
||
name: nginx
|
||
state: present
|
||
- name: 启动服务
|
||
service:
|
||
name: nginx
|
||
state: started`
|
||
}
|
||
]
|
||
|
||
function getToolByName(name) {
|
||
return tools.find(t => t.name === name)
|
||
}
|
||
|
||
function toggleTool(name) {
|
||
const idx = selectedTools.value.indexOf(name)
|
||
if (idx >= 0) {
|
||
if (selectedTools.value.length > 2) {
|
||
selectedTools.value.splice(idx, 1)
|
||
}
|
||
} else {
|
||
selectedTools.value.push(name)
|
||
}
|
||
}
|
||
|
||
function getCellClass(toolName, featureKey) {
|
||
const val = getToolByName(toolName).features[featureKey]
|
||
if (featureKey === 'multiCloud') {
|
||
if (val.includes('原生多云')) return 'cell-good'
|
||
if (val.includes('仅')) return 'cell-warn'
|
||
return ''
|
||
}
|
||
if (featureKey === 'learning') {
|
||
if (val === '低') return 'cell-good'
|
||
if (val.includes('高')) return 'cell-warn'
|
||
return ''
|
||
}
|
||
return ''
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.iac-tool-comparison-demo {
|
||
border: 1px solid var(--vp-c-divider);
|
||
border-radius: 8px;
|
||
background: var(--vp-c-bg-soft);
|
||
padding: 1rem 1.2rem;
|
||
margin: 1rem 0;
|
||
}
|
||
.demo-label {
|
||
font-size: 0.78rem;
|
||
font-weight: bold;
|
||
color: var(--vp-c-text-2);
|
||
margin-bottom: 1rem;
|
||
text-align: center;
|
||
}
|
||
.selector-hint {
|
||
font-size: 0.78rem;
|
||
color: var(--vp-c-text-3);
|
||
display: block;
|
||
margin-bottom: 0.5rem;
|
||
}
|
||
.tool-chips {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 0.4rem;
|
||
margin-bottom: 1rem;
|
||
}
|
||
.tool-chip {
|
||
padding: 5px 14px;
|
||
border: 1px solid var(--vp-c-divider);
|
||
border-radius: 20px;
|
||
background: var(--vp-c-bg);
|
||
cursor: pointer;
|
||
font-size: 0.8rem;
|
||
transition: all 0.2s;
|
||
}
|
||
.tool-chip:hover { transform: scale(1.05); }
|
||
.comparison-grid { overflow-x: auto; margin-bottom: 1rem; }
|
||
.comparison-grid table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
font-size: 0.78rem;
|
||
}
|
||
.comparison-grid th,
|
||
.comparison-grid td {
|
||
padding: 8px 10px;
|
||
border: 1px solid var(--vp-c-divider);
|
||
text-align: center;
|
||
}
|
||
.comparison-grid th {
|
||
background: var(--vp-c-bg-alt);
|
||
font-weight: 600;
|
||
}
|
||
.feature-col { text-align: left; min-width: 80px; }
|
||
.feature-cell { font-weight: 600; text-align: left; }
|
||
.tool-header-icon { margin-right: 4px; }
|
||
.cell-good { color: #10b981; font-weight: 600; }
|
||
.cell-warn { color: #f59e0b; }
|
||
.empty-hint {
|
||
text-align: center;
|
||
padding: 2rem;
|
||
color: var(--vp-c-text-3);
|
||
font-size: 0.85rem;
|
||
}
|
||
.detail-hint {
|
||
text-align: center;
|
||
font-size: 0.75rem;
|
||
color: var(--vp-c-text-3);
|
||
margin-bottom: 0.5rem;
|
||
}
|
||
.tool-detail-btns {
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: 0.5rem;
|
||
margin-bottom: 0.5rem;
|
||
}
|
||
.detail-btn {
|
||
padding: 4px 12px;
|
||
border: 1px solid var(--vp-c-divider);
|
||
border-radius: 6px;
|
||
background: var(--vp-c-bg);
|
||
cursor: pointer;
|
||
font-size: 0.78rem;
|
||
transition: all 0.2s;
|
||
}
|
||
.detail-btn.active {
|
||
background: var(--vp-c-brand);
|
||
color: #fff;
|
||
border-color: var(--vp-c-brand);
|
||
}
|
||
.detail-card {
|
||
border: 1px solid var(--vp-c-divider);
|
||
border-radius: 8px;
|
||
padding: 1rem;
|
||
background: var(--vp-c-bg);
|
||
margin-top: 0.5rem;
|
||
}
|
||
.detail-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
margin-bottom: 0.5rem;
|
||
}
|
||
.detail-icon { font-size: 1.2rem; }
|
||
.detail-name { font-weight: 600; font-size: 1rem; flex: 1; }
|
||
.close-btn {
|
||
background: none;
|
||
border: none;
|
||
cursor: pointer;
|
||
font-size: 1rem;
|
||
color: var(--vp-c-text-3);
|
||
}
|
||
.detail-desc {
|
||
font-size: 0.82rem;
|
||
color: var(--vp-c-text-2);
|
||
line-height: 1.6;
|
||
margin-bottom: 0.8rem;
|
||
}
|
||
.code-label {
|
||
font-size: 0.72rem;
|
||
color: var(--vp-c-text-3);
|
||
margin-bottom: 4px;
|
||
}
|
||
.code-block {
|
||
background: #1a1a2e;
|
||
color: #e0e0e0;
|
||
padding: 0.8rem;
|
||
border-radius: 6px;
|
||
font-size: 0.73rem;
|
||
font-family: 'Menlo', 'Consolas', monospace;
|
||
line-height: 1.5;
|
||
overflow-x: auto;
|
||
margin: 0;
|
||
}
|
||
.fade-enter-active, .fade-leave-active { transition: opacity 0.25s; }
|
||
.fade-enter-from, .fade-leave-to { opacity: 0; }
|
||
</style>
|