feat(appendix): 添加多个交互式演示组件,完善 AI/Infra 等章节内容

- 新增 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 内容,集成交互式演示
This commit is contained in:
sanbuphy
2026-02-24 18:22:58 +08:00
parent b5a55811cc
commit 3af119a598
86 changed files with 20311 additions and 340 deletions
@@ -0,0 +1,412 @@
<!--
PostmortemDemo.vue
事后复盘演示交互式展示"五个为什么"分析法和复盘报告模板
-->
<template>
<div class="postmortem-demo">
<div class="header">
<div class="title">事后复盘五个为什么 (5 Whys Analysis)</div>
<div class="subtitle">点击"继续追问"层层深入挖掘根本原因</div>
</div>
<div class="case-select">
<button
v-for="c in cases"
:key="c.id"
:class="['case-btn', { active: activeCase === c.id }]"
@click="selectCase(c.id)"
>
{{ c.name }}
</button>
</div>
<div v-if="currentCase" class="whys-chain">
<div
v-for="(why, index) in visibleWhys"
:key="index"
class="why-item"
>
<div class="why-header">
<span class="why-badge">
{{ index === 0 ? '现象' : '第 ' + index + ' 个为什么' }}
</span>
<span class="why-depth">
深度 {{ index }} / {{ currentCase.whys.length - 1 }}
</span>
</div>
<div class="why-question" v-if="index > 0">
为什么{{ currentCase.whys[index - 1].answer }}
</div>
<div class="why-answer">
<span class="answer-icon">{{ index === currentCase.whys.length - 1 && revealedCount >= currentCase.whys.length ? '🎯' : '💡' }}</span>
<span>{{ why.answer }}</span>
</div>
<div
v-if="index < visibleWhys.length - 1"
class="why-arrow"
>
继续追问
</div>
</div>
<div class="why-controls" v-if="revealedCount < currentCase.whys.length">
<button class="ask-btn" @click="revealNext">
继续追问为什么
</button>
</div>
<div v-else class="root-cause-box">
<div class="root-label">根本原因已找到</div>
<div class="root-content">{{ currentCase.rootCause }}</div>
<div class="root-actions">
<div class="actions-label">改进措施</div>
<div
v-for="(action, i) in currentCase.actions"
:key="i"
class="action-item"
>
<span class="action-check">&#10003;</span>
<span>{{ action }}</span>
</div>
</div>
</div>
</div>
<div class="template-box">
<div class="template-title">复盘报告模板</div>
<div class="template-sections">
<div
v-for="(section, i) in templateSections"
:key="i"
class="template-item"
:class="{ expanded: expandedSection === i }"
@click="expandedSection = expandedSection === i ? -1 : i"
>
<div class="template-item-header">
<span class="template-num">{{ i + 1 }}</span>
<span class="template-name">{{ section.name }}</span>
<span class="template-toggle">
{{ expandedSection === i ? '' : '+' }}
</span>
</div>
<div v-if="expandedSection === i" class="template-item-body">
{{ section.desc }}
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const activeCase = ref('payment')
const revealedCount = ref(1)
const expandedSection = ref(-1)
const casesData = {
payment: {
id: 'payment',
name: '支付系统宕机',
whys: [
{ answer: '支付系统在高峰期完全不可用,持续 18 分钟' },
{ answer: '数据库连接池被耗尽,所有新请求排队超时' },
{ answer: '一条慢查询占用连接长达 30 秒不释放' },
{ answer: '新上线的对账功能执行了全表扫描,没有使用索引' },
{ answer: '代码审查时没有检查 SQL 执行计划,也没有慢查询测试环节' }
],
rootCause: '研发流程缺陷:代码审查清单中缺少 SQL 性能审查项,CI/CD 流水线中没有慢查询检测环节。',
actions: [
'代码审查清单增加"SQL 执行计划检查"必选项',
'CI 流水线增加慢查询自动检测(阈值 100ms)',
'数据库连接池增加单查询超时限制(5s 强制断开)',
'建立大表变更审批流程'
]
},
deploy: {
id: 'deploy',
name: '部署导致服务中断',
whys: [
{ answer: '新版本部署后,用户登录功能完全失效,持续 25 分钟' },
{ answer: '新版本的认证服务无法连接 Redis 缓存集群' },
{ answer: '部署脚本使用了错误的 Redis 集群地址(指向了测试环境)' },
{ answer: '环境配置是硬编码在部署脚本中的,没有使用配置中心' },
{ answer: '团队没有统一的配置管理规范,每个服务自行管理配置' }
],
rootCause: '基础设施缺陷:缺乏统一的配置管理平台和规范,环境配置散落在各处,容易出错且难以审计。',
actions: [
'引入配置中心(如 Consul/Nacos),统一管理所有环境配置',
'部署流水线增加配置校验步骤(连通性检查)',
'禁止在代码和脚本中硬编码环境地址',
'建立部署前 Checklist,包含配置确认环节'
]
}
}
const cases = [
{ id: 'payment', name: '支付系统宕机' },
{ id: 'deploy', name: '部署导致服务中断' }
]
const currentCase = computed(() => casesData[activeCase.value] || null)
const visibleWhys = computed(() => {
if (!currentCase.value) return []
return currentCase.value.whys.slice(0, revealedCount.value)
})
const selectCase = (id) => {
activeCase.value = id
revealedCount.value = 1
}
const revealNext = () => {
if (currentCase.value && revealedCount.value < currentCase.value.whys.length) {
revealedCount.value++
}
}
const templateSections = [
{ name: '事故概述', desc: '简要描述事故发生的时间、持续时长、影响范围和严重程度。例如:"2024年3月15日 14:02-14:20,支付服务完全不可用,影响约 12 万笔交易。"' },
{ name: '时间线', desc: '按时间顺序记录从发现到解决的每一个关键事件,精确到分钟。包括:告警触发、人员响应、排查过程、修复操作、服务恢复等。' },
{ name: '影响评估', desc: '量化事故影响:受影响用户数、失败请求数、经济损失估算、SLA 影响等。用数据说话,避免模糊描述。' },
{ name: '根因分析', desc: '使用"五个为什么"等方法深入分析根本原因。区分直接原因(触发因素)和根本原因(系统性缺陷)。' },
{ name: '改进措施', desc: '列出具体的改进行动项,每项必须有负责人和截止日期。分为短期(本周)、中期(本月)、长期(本季度)三个层次。' },
{ name: '经验教训', desc: '总结哪些做得好(值得保持)、哪些做得不好(需要改进)、哪些是意外发现(新的风险点)。' }
]
</script>
<style scoped>
.postmortem-demo {
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg-soft);
border-radius: 12px;
padding: 1.5rem;
margin: 1.5rem 0;
}
.header { margin-bottom: 1.5rem; }
.title { font-weight: 700; font-size: 1.1rem; margin-bottom: 0.25rem; }
.subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
.case-select {
display: flex;
gap: 0.5rem;
margin-bottom: 1.5rem;
flex-wrap: wrap;
}
.case-btn {
padding: 0.5rem 1rem;
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg);
border-radius: 6px;
cursor: pointer;
font-size: 0.9rem;
transition: all 0.2s;
}
.case-btn:hover {
border-color: var(--vp-c-brand);
color: var(--vp-c-brand);
}
.case-btn.active {
background: var(--vp-c-brand);
color: #fff;
border-color: var(--vp-c-brand);
}
.whys-chain {
margin-bottom: 1.5rem;
}
.why-item {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
padding: 0.75rem;
margin-bottom: 0.25rem;
}
.why-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 0.4rem;
}
.why-badge {
font-weight: 700;
font-size: 0.8rem;
padding: 0.15rem 0.5rem;
background: var(--vp-c-brand);
color: #fff;
border-radius: 4px;
}
.why-depth {
font-size: 0.75rem;
color: var(--vp-c-text-3);
}
.why-question {
font-size: 0.85rem;
color: var(--vp-c-text-2);
font-style: italic;
margin-bottom: 0.3rem;
padding-left: 0.5rem;
border-left: 2px solid var(--vp-c-divider);
}
.why-answer {
display: flex;
align-items: flex-start;
gap: 0.4rem;
font-size: 0.9rem;
line-height: 1.5;
}
.answer-icon { flex-shrink: 0; }
.why-arrow {
text-align: center;
color: var(--vp-c-text-3);
font-size: 0.8rem;
padding: 0.25rem 0;
}
.why-controls {
text-align: center;
margin-top: 0.75rem;
}
.ask-btn {
padding: 0.6rem 1.5rem;
background: var(--vp-c-brand);
color: #fff;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 0.9rem;
font-weight: 600;
transition: all 0.2s;
}
.ask-btn:hover { opacity: 0.9; transform: translateY(-1px); }
.root-cause-box {
background: rgba(34, 197, 94, 0.08);
border: 2px solid #22c55e;
border-radius: 10px;
padding: 1rem;
margin-top: 0.75rem;
}
.root-label {
font-weight: 700;
font-size: 0.95rem;
color: #22c55e;
margin-bottom: 0.5rem;
}
.root-content {
font-size: 0.9rem;
line-height: 1.6;
margin-bottom: 0.75rem;
}
.actions-label {
font-weight: 600;
font-size: 0.85rem;
margin-bottom: 0.4rem;
}
.action-item {
display: flex;
align-items: flex-start;
gap: 0.4rem;
font-size: 0.85rem;
margin-bottom: 0.3rem;
}
.action-check {
color: #22c55e;
font-weight: 700;
flex-shrink: 0;
}
.template-box {
background: var(--vp-c-bg);
border-radius: 10px;
padding: 1rem;
border: 1px solid var(--vp-c-divider);
}
.template-title {
font-weight: 700;
font-size: 0.95rem;
margin-bottom: 0.75rem;
}
.template-sections {
display: flex;
flex-direction: column;
gap: 0.4rem;
}
.template-item {
border: 1px solid var(--vp-c-divider);
border-radius: 6px;
cursor: pointer;
transition: all 0.2s;
overflow: hidden;
}
.template-item:hover {
border-color: var(--vp-c-brand);
}
.template-item.expanded {
border-color: var(--vp-c-brand);
}
.template-item-header {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 0.75rem;
}
.template-num {
width: 22px; height: 22px; border-radius: 50%;
background: var(--vp-c-bg-soft);
display: flex; align-items: center; justify-content: center;
font-size: 0.75rem; font-weight: 700; flex-shrink: 0;
}
.template-name {
flex: 1;
font-weight: 600;
font-size: 0.9rem;
}
.template-toggle {
font-size: 1.1rem;
color: var(--vp-c-text-3);
font-weight: 700;
}
.template-item-body {
padding: 0 0.75rem 0.6rem;
font-size: 0.85rem;
color: var(--vp-c-text-2);
line-height: 1.6;
}
@media (max-width: 768px) {
.case-select { flex-direction: column; }
.case-btn { width: 100%; }
}
</style>