feat: add 30 interactive components for computer fundamentals section
Added comprehensive interactive demos covering: - Operating systems (2): OS overview, process/memory/filesystem - Data encoding (2): data lifecycle, encoding/storage/transmission - Network basics (5): overview, physical/data-link/transport/application layers - Data structures (5): overview, linear structures, hash table, tree, selector - Algorithms (4): overview, recursion, greedy thinking, paradigms - Programming languages (5): evolution, paradigms, scenarios, comparison, type models - Compilers (2): analogy, practice demo - Additional (5): search/sort algorithms, network principles, encoding basics, storage hierarchy, graph structures Also updated component registration in theme index.js and fixed minor formatting issues in related docs.
This commit is contained in:
+514
@@ -0,0 +1,514 @@
|
||||
<template>
|
||||
<div class="algorithm-overview-demo">
|
||||
<div class="demo-header">
|
||||
<span class="icon">🧮</span>
|
||||
<span class="title">算法思维入门</span>
|
||||
<span class="subtitle">解决问题的一套步骤和方法</span>
|
||||
</div>
|
||||
|
||||
<div class="analogy-box">
|
||||
<div class="analogy-content">
|
||||
<div class="analogy-icon">📖</div>
|
||||
<div class="analogy-text">
|
||||
<strong>算法就像菜谱:</strong><br>
|
||||
食材 = 数据<br>
|
||||
烹饪步骤 = 算法<br>
|
||||
美味菜肴 = 结果
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="algorithm-categories">
|
||||
<div class="category-title">常见算法类型</div>
|
||||
<div class="category-grid">
|
||||
<div
|
||||
v-for="category in categories"
|
||||
:key="category.id"
|
||||
:class="['category-card', { active: activeCategory === category.id }]"
|
||||
@click="activeCategory = category.id"
|
||||
>
|
||||
<div class="card-icon">{{ category.icon }}</div>
|
||||
<div class="card-name">{{ category.name }}</div>
|
||||
<div class="card-desc">{{ category.desc }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 算法详解 -->
|
||||
<div v-if="activeCategory" class="algorithm-detail">
|
||||
<div class="detail-header">
|
||||
<span class="detail-icon">{{ currentCategory.icon }}</span>
|
||||
<span class="detail-title">{{ currentCategory.name }}</span>
|
||||
</div>
|
||||
|
||||
<div class="detail-content">
|
||||
<div class="detail-section">
|
||||
<div class="section-title">核心思想</div>
|
||||
<div class="section-text">{{ currentCategory.idea }}</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-section">
|
||||
<div class="section-title">生活类比</div>
|
||||
<div class="analogy-card">
|
||||
<div class="analogy-scenario">{{ currentCategory.analogy.scenario }}</div>
|
||||
<div class="analogy-explanation">{{ currentCategory.analogy.explanation }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-section">
|
||||
<div class="section-title">时间复杂度</div>
|
||||
<div class="complexity-display">
|
||||
<div class="complexity-bigO">{{ currentCategory.complexity }}</div>
|
||||
<div class="complexity-desc">{{ currentCategory.complexityDesc }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-section">
|
||||
<div class="section-title">典型应用</div>
|
||||
<div class="app-list">
|
||||
<div
|
||||
v-for="(app, index) in currentCategory.applications"
|
||||
:key="index"
|
||||
class="app-tag"
|
||||
>
|
||||
{{ app }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 复杂度对比 -->
|
||||
<div class="complexity-comparison">
|
||||
<div class="comparison-title">常见算法复杂度对比</div>
|
||||
<div class="comparison-chart">
|
||||
<div
|
||||
v-for="(item, index) in complexityChart"
|
||||
:key="index"
|
||||
class="chart-item"
|
||||
>
|
||||
<div class="chart-label">{{ item.name }}</div>
|
||||
<div class="chart-bar-container">
|
||||
<div
|
||||
class="chart-bar"
|
||||
:style="{ width: item.width, backgroundColor: item.color }"
|
||||
></div>
|
||||
</div>
|
||||
<div class="chart-value">{{ item.complexity }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 学习建议 -->
|
||||
<div class="learning-tips">
|
||||
<div class="tips-title">算法学习建议</div>
|
||||
<div class="tips-grid">
|
||||
<div class="tip-card">
|
||||
<div class="tip-icon">📚</div>
|
||||
<div class="tip-title">理解优先</div>
|
||||
<div class="tip-desc">先理解算法思想,再关注代码实现</div>
|
||||
</div>
|
||||
<div class="tip-card">
|
||||
<div class="tip-icon">✏️</div>
|
||||
<div class="tip-title">动手实践</div>
|
||||
<div class="tip-desc">自己实现一遍,加深理解</div>
|
||||
</div>
|
||||
<div class="tip-card">
|
||||
<div class="tip-icon">🔄</div>
|
||||
<div class="tip-title">多次练习</div>
|
||||
<div class="tip-desc">不同场景反复应用同一算法</div>
|
||||
</div>
|
||||
<div class="tip-card">
|
||||
<div class="tip-icon">⚡</div>
|
||||
<div class="tip-title">分析优化</div>
|
||||
<div class="tip-desc">思考时间和空间复杂度,寻找优化方案</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const activeCategory = ref('search')
|
||||
|
||||
const categories = [
|
||||
{
|
||||
id: 'search',
|
||||
name: '查找算法',
|
||||
icon: '🔍',
|
||||
desc: '在一堆数据中找到目标',
|
||||
idea: '从数据集合中找到特定元素的过程',
|
||||
analogy: {
|
||||
scenario: '在字典里查单词',
|
||||
explanation: '顺序查找 = 从第一页翻到最后一页;二分查找 = 直接翻到中间,判断在前半还是后半'
|
||||
},
|
||||
complexity: 'O(log n)',
|
||||
complexityDesc: '二分查找非常快,每次排除一半数据',
|
||||
applications: ['搜索引擎', '数据库查询', '自动补全']
|
||||
},
|
||||
{
|
||||
id: 'sort',
|
||||
name: '排序算法',
|
||||
icon: '📊',
|
||||
desc: '把数据按顺序排列',
|
||||
idea: '将无序数据重新排列成有序序列',
|
||||
analogy: {
|
||||
scenario: '整理扑克牌',
|
||||
explanation: '插入排序 = 每次拿一张牌插到正确的位置;快速排序 = 把牌分成大小两堆,递归整理'
|
||||
},
|
||||
complexity: 'O(n log n)',
|
||||
complexityDesc: '快速排序、归并排序是最高效的通用排序算法',
|
||||
applications: ['排行榜', '文件排序', '数据可视化']
|
||||
},
|
||||
{
|
||||
id: 'recursive',
|
||||
name: '递归算法',
|
||||
icon: '🔄',
|
||||
desc: '自己调用自己',
|
||||
idea: '将大问题分解为相同类型的小问题',
|
||||
analogy: {
|
||||
scenario: '俄罗斯套娃',
|
||||
explanation: '打开一个大娃娃,里面有个小一点的娃娃,再打开还有更小的...直到最小的一个'
|
||||
},
|
||||
complexity: 'O(log n) 到 O(2ⁿ)',
|
||||
complexityDesc: '取决于问题类型,二分查找递归很快,斐波那契递归较慢',
|
||||
applications: ['树遍历', '分治算法', '动态规划']
|
||||
},
|
||||
{
|
||||
id: 'greedy',
|
||||
name: '贪心算法',
|
||||
icon: '🎯',
|
||||
desc: '每步都选当前最优',
|
||||
idea: '在每一步选择中都采取当前状态下最优的选择',
|
||||
analogy: {
|
||||
scenario: '找零钱',
|
||||
explanation: '找 37 元零钱:先拿一张 20(最大可能),再拿 10、5、1、1,每次都选最大的面值'
|
||||
},
|
||||
complexity: 'O(n) 或 O(n log n)',
|
||||
complexityDesc: '通常很快,但可能得不到全局最优解',
|
||||
applications: ['最短路径', '背包问题', '任务调度']
|
||||
},
|
||||
{
|
||||
id: 'dynamic',
|
||||
name: '动态规划',
|
||||
icon: '📈',
|
||||
desc: '保存中间结果避免重复',
|
||||
idea: '将复杂问题分解为子问题,保存子问题的解',
|
||||
analogy: {
|
||||
scenario: '爬楼梯',
|
||||
explanation: '要爬到第 n 级,可以从 n-1 级跨 1 步,或从 n-2 级跨 2 步,记住之前的结果避免重复计算'
|
||||
},
|
||||
complexity: 'O(n²) 或 O(n³)',
|
||||
complexityDesc: '用空间换时间,比递归快很多',
|
||||
applications: ['最短路径', '背包问题', '字符串匹配']
|
||||
}
|
||||
]
|
||||
|
||||
const complexityChart = [
|
||||
{ name: '二分查找', complexity: 'O(log n)', width: '10%', color: '#10b981' },
|
||||
{ name: '快速排序', complexity: 'O(n log n)', width: '25%', color: '#3b82f6' },
|
||||
{ name: '插入排序', complexity: 'O(n²)', width: '50%', color: '#f59e0b' },
|
||||
{ name: '暴力递归', complexity: 'O(2ⁿ)', width: '100%', color: '#ef4444' }
|
||||
]
|
||||
|
||||
const currentCategory = computed(() => categories.find(c => c.id === activeCategory.value))
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.algorithm-overview-demo {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.demo-header .icon { font-size: 1.5rem; }
|
||||
.demo-header .title { font-weight: 700; font-size: 1.1rem; }
|
||||
.demo-header .subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
|
||||
|
||||
.analogy-box {
|
||||
background: var(--vp-c-bg);
|
||||
border-left: 4px solid var(--vp-c-brand);
|
||||
border-radius: 6px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.analogy-content {
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.analogy-icon {
|
||||
font-size: 3rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.analogy-text {
|
||||
font-size: 1rem;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.algorithm-categories {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.category-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.category-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.category-card {
|
||||
padding: 1.25rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.category-card:hover {
|
||||
border-color: var(--vp-c-brand);
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
|
||||
.category-card.active {
|
||||
border-color: var(--vp-c-brand);
|
||||
background: var(--vp-c-brand-soft);
|
||||
}
|
||||
|
||||
.card-icon {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.card-name {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.card-desc {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.algorithm-detail {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.detail-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 1.5rem;
|
||||
padding-bottom: 1rem;
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.detail-icon {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.detail-title {
|
||||
font-weight: 600;
|
||||
font-size: 1.1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.detail-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.detail-section {}
|
||||
|
||||
.section-title {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 0.75rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.section-text {
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.analogy-card {
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.analogy-scenario {
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0.5rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.analogy-explanation {
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.6;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.complexity-display {
|
||||
text-align: center;
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.complexity-bigO {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
color: var(--vp-c-brand);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.complexity-desc {
|
||||
font-size: 0.85rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.app-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.app-tag {
|
||||
padding: 0.5rem 0.75rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 20px;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.complexity-comparison {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.comparison-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.comparison-chart {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.chart-item {
|
||||
display: grid;
|
||||
grid-template-columns: 100px 1fr 80px;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.chart-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.chart-label {
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.chart-bar-container {
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 4px;
|
||||
height: 24px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.chart-bar {
|
||||
height: 100%;
|
||||
transition: width 0.5s ease-out;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.chart-value {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.learning-tips {
|
||||
border-top: 1px solid var(--vp-c-divider);
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
|
||||
.tips-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.tips-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.tip-card {
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tip-icon {
|
||||
font-size: 1.8rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.tip-title {
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.tip-desc {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
line-height: 1.4;
|
||||
}
|
||||
</style>
|
||||
+490
@@ -0,0 +1,490 @@
|
||||
<template>
|
||||
<div class="algorithm-paradigm-demo">
|
||||
<div class="demo-header">
|
||||
<span class="icon">🔧</span>
|
||||
<span class="title">算法设计范式</span>
|
||||
<span class="subtitle">解决问题的常用套路</span>
|
||||
</div>
|
||||
|
||||
<div class="intro-text">
|
||||
算法设计范式是解决问题的<strong>通用策略</strong>,掌握这些套路可以快速找到解题思路
|
||||
</div>
|
||||
|
||||
<div class="paradigm-grid">
|
||||
<div
|
||||
v-for="paradigm in paradigms"
|
||||
:key="paradigm.id"
|
||||
:class="['paradigm-card', { active: activeParadigm === paradigm.id }]"
|
||||
@click="activeParadigm = paradigm.id"
|
||||
>
|
||||
<div class="card-icon">{{ paradigm.icon }}</div>
|
||||
<div class="card-name">{{ paradigm.name }}</div>
|
||||
<div class="card-tagline">{{ paradigm.tagline }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 详细说明 -->
|
||||
<div v-if="activeParadigm" class="paradigm-detail">
|
||||
<div class="detail-header">
|
||||
<span class="detail-icon">{{ currentParadigm.icon }}</span>
|
||||
<span class="detail-title">{{ currentParadigm.name }}</span>
|
||||
</div>
|
||||
|
||||
<div class="detail-content">
|
||||
<div class="detail-section">
|
||||
<div class="section-title">核心思想</div>
|
||||
<div class="section-text">{{ currentParadigm.idea }}</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-section">
|
||||
<div class="section-title">适用场景</div>
|
||||
<div class="scenario-tags">
|
||||
<span
|
||||
v-for="(scenario, index) in currentParadigm.scenarios"
|
||||
:key="index"
|
||||
class="scenario-tag"
|
||||
>
|
||||
{{ scenario }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-section">
|
||||
<div class="section-title">经典问题</div>
|
||||
<div class="problems-list">
|
||||
<div
|
||||
v-for="(problem, index) in currentParadigm.problems"
|
||||
:key="index"
|
||||
class="problem-item"
|
||||
>
|
||||
<div class="problem-icon">📝</div>
|
||||
<div class="problem-text">{{ problem }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-section">
|
||||
<div class="section-title">时间复杂度</div>
|
||||
<div class="complexity-box">
|
||||
<div class="complexity-value">{{ currentParadigm.complexity }}</div>
|
||||
<div class="complexity-note">{{ currentParadigm.complexityNote }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 对比总结 -->
|
||||
<div class="paradigm-comparison">
|
||||
<div class="comparison-title">范式对比总结</div>
|
||||
<table class="comparison-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>范式</th>
|
||||
<th>核心策略</th>
|
||||
<th>最优性</th>
|
||||
<th>适用场景</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="(item, index) in comparisonData"
|
||||
:key="index"
|
||||
:class="{ highlighted: item.id === activeParadigm }"
|
||||
>
|
||||
<td>{{ item.icon }} {{ item.name }}</td>
|
||||
<td>{{ item.strategy }}</td>
|
||||
<td>{{ item.optimal }}</td>
|
||||
<td>{{ item.use }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- 选择建议 -->
|
||||
<div class="selection-guide">
|
||||
<div class="guide-title">如何选择合适的范式?</div>
|
||||
<div class="guide-steps">
|
||||
<div class="guide-step">
|
||||
<div class="step-number">1</div>
|
||||
<div class="step-content">
|
||||
<div class="step-title">分析问题特征</div>
|
||||
<div class="step-desc">是否有重叠子问题?是否有最优子结构?</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="guide-step">
|
||||
<div class="step-number">2</div>
|
||||
<div class="step-content">
|
||||
<div class="step-title">判断是否需要最优解</div>
|
||||
<div class="step-desc">贪心不一定最优,动态规划保证最优</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="guide-step">
|
||||
<div class="step-number">3</div>
|
||||
<div class="step-content">
|
||||
<div class="step-title">考虑数据规模</div>
|
||||
<div class="step-desc">回溯适合小规模,分治适合大规模</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const activeParadigm = ref('divide')
|
||||
|
||||
const paradigms = [
|
||||
{
|
||||
id: 'divide',
|
||||
name: '分治法',
|
||||
icon: '✂️',
|
||||
tagline: '分而治之',
|
||||
idea: '将大问题分解成多个小问题,递归解决小问题,最后合并结果',
|
||||
scenarios: ['数组排序', '矩阵乘法', '大整数运算'],
|
||||
problems: ['归并排序', '快速排序', '二分查找', 'Strassen 矩阵乘法'],
|
||||
complexity: 'O(n log n)',
|
||||
complexityNote: '通常比暴力法快很多'
|
||||
},
|
||||
{
|
||||
id: 'dynamic',
|
||||
name: '动态规划',
|
||||
icon: '📊',
|
||||
tagline: '保存结果避免重复',
|
||||
idea: '将问题分解为重叠子问题,保存子问题的解,避免重复计算',
|
||||
scenarios: ['最优解问题', '计数问题', '路径问题'],
|
||||
problems: ['斐波那契数列', '背包问题', '最长公共子序列', '最短路径'],
|
||||
complexity: 'O(n²) 或 O(n³)',
|
||||
complexityNote: '用空间换时间,比递归快'
|
||||
},
|
||||
{
|
||||
id: 'greedy',
|
||||
name: '贪心法',
|
||||
icon: '🎯',
|
||||
tagline: '局部最优',
|
||||
idea: '在每一步选择中都采取当前状态下最优的选择,希望达到全局最优',
|
||||
scenarios: ['优化问题', '调度问题', '图问题'],
|
||||
problems: ['找零钱', '活动选择', 'Huffman 编码', '最小生成树'],
|
||||
complexity: 'O(n log n)',
|
||||
complexityNote: '最快,但不一定最优'
|
||||
},
|
||||
{
|
||||
id: 'backtrack',
|
||||
name: '回溯法',
|
||||
icon: '🔙',
|
||||
tagline: '试错法',
|
||||
idea: '系统性地搜索解空间,遇到死路就回退到上一个分岔口',
|
||||
scenarios: ['组合问题', '排列问题', '约束满足'],
|
||||
problems: ['N 皇后问题', '数独', '全排列', '子集问题'],
|
||||
complexity: 'O(2ⁿ) 或 O(n!)',
|
||||
complexityNote: '指数级,适合小规模'
|
||||
}
|
||||
]
|
||||
|
||||
const comparisonData = [
|
||||
{
|
||||
id: 'divide',
|
||||
name: '分治法',
|
||||
icon: '✂️',
|
||||
strategy: '分解 → 递归 → 合并',
|
||||
optimal: '保证最优',
|
||||
use: '问题可独立分解'
|
||||
},
|
||||
{
|
||||
id: 'dynamic',
|
||||
name: '动态规划',
|
||||
icon: '📊',
|
||||
strategy: '保存子问题解',
|
||||
optimal: '保证最优',
|
||||
use: '有重叠子问题'
|
||||
},
|
||||
{
|
||||
id: 'greedy',
|
||||
name: '贪心法',
|
||||
icon: '🎯',
|
||||
strategy: '每次选最优',
|
||||
optimal: '不一定最优',
|
||||
use: '局部最优 → 全局最优'
|
||||
},
|
||||
{
|
||||
id: 'backtrack',
|
||||
name: '回溯法',
|
||||
icon: '🔙',
|
||||
strategy: '深度优先搜索',
|
||||
optimal: '保证最优',
|
||||
use: '解空间小,需要穷举'
|
||||
}
|
||||
]
|
||||
|
||||
const currentParadigm = computed(() => paradigms.find(p => p.id === activeParadigm.value))
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.algorithm-paradigm-demo {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.demo-header .icon { font-size: 1.5rem; }
|
||||
.demo-header .title { font-weight: 700; font-size: 1.1rem; }
|
||||
.demo-header .subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
|
||||
|
||||
.intro-text {
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg);
|
||||
border-left: 4px solid var(--vp-c-brand);
|
||||
border-radius: 6px;
|
||||
margin-bottom: 2rem;
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.paradigm-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 1rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.paradigm-card {
|
||||
padding: 1.25rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.paradigm-card:hover {
|
||||
border-color: var(--vp-c-brand);
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
|
||||
.paradigm-card.active {
|
||||
border-color: var(--vp-c-brand);
|
||||
background: var(--vp-c-brand-soft);
|
||||
}
|
||||
|
||||
.card-icon {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.card-name {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.card-tagline {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.paradigm-detail {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.detail-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 1.5rem;
|
||||
padding-bottom: 1rem;
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.detail-icon {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.detail-title {
|
||||
font-weight: 600;
|
||||
font-size: 1.1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.detail-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.detail-section {}
|
||||
|
||||
.section-title {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 0.75rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.section-text {
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.scenario-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.scenario-tag {
|
||||
padding: 0.5rem 0.75rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 20px;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.problems-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.problem-item {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
padding: 0.75rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.problem-icon {
|
||||
font-size: 1rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.problem-text {
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.complexity-box {
|
||||
text-align: center;
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.complexity-value {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 1.3rem;
|
||||
font-weight: 700;
|
||||
color: var(--vp-c-brand);
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.complexity-note {
|
||||
font-size: 0.85rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.paradigm-comparison {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.comparison-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.comparison-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.comparison-table th {
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
padding: 0.75rem;
|
||||
text-align: center;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.comparison-table td {
|
||||
padding: 0.75rem;
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
tr.highlighted {
|
||||
background: var(--vp-c-brand-soft);
|
||||
}
|
||||
|
||||
.selection-guide {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.guide-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.guide-steps {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.guide-step {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.step-number {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.step-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.step-title {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.step-desc {
|
||||
font-size: 0.85rem;
|
||||
color: var(--vp-c-text-2);
|
||||
line-height: 1.5;
|
||||
}
|
||||
</style>
|
||||
+531
@@ -0,0 +1,531 @@
|
||||
<template>
|
||||
<div class="application-layer-demo">
|
||||
<div class="demo-header">
|
||||
<span class="icon">🌍</span>
|
||||
<span class="title">应用层:为你服务的各种协议</span>
|
||||
<span class="subtitle">HTTP、DNS、DHCP 等协议如何工作</span>
|
||||
</div>
|
||||
|
||||
<div class="protocol-gallery">
|
||||
<div
|
||||
v-for="protocol in protocols"
|
||||
:key="protocol.id"
|
||||
:class="['protocol-card', { active: activeProtocol === protocol.id }]"
|
||||
@click="activeProtocol = protocol.id"
|
||||
>
|
||||
<div class="card-icon">{{ protocol.icon }}</div>
|
||||
<div class="card-name">{{ protocol.name }}</div>
|
||||
<div class="card-desc">{{ protocol.desc }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 协议详情 -->
|
||||
<div class="protocol-detail">
|
||||
<div class="detail-header">
|
||||
<span class="detail-icon">{{ currentProtocol.icon }}</span>
|
||||
<span class="detail-title">{{ currentProtocol.name }} 协议</span>
|
||||
</div>
|
||||
|
||||
<div class="detail-content">
|
||||
<div class="detail-section">
|
||||
<div class="section-title">作用</div>
|
||||
<div class="section-text">{{ currentProtocol.purpose }}</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-section">
|
||||
<div class="section-title">工作原理</div>
|
||||
<div class="section-steps">
|
||||
<div v-for="(step, index) in currentProtocol.steps" :key="index" class="step-item">
|
||||
<span class="step-num">{{ index + 1 }}</span>
|
||||
<span class="step-text">{{ step }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-section">
|
||||
<div class="section-title">日常应用</div>
|
||||
<div class="app-list">
|
||||
<div v-for="(app, index) in currentProtocol.apps" :key="index" class="app-tag">
|
||||
{{ app.icon }} {{ app.name }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- HTTP 请求响应示例 -->
|
||||
<div v-if="activeProtocol === 'http'" class="http-example">
|
||||
<div class="example-title">HTTP 请求/响应示例</div>
|
||||
<div class="example-content">
|
||||
<div class="request-response">
|
||||
<div class="request-box">
|
||||
<div class="box-header">📤 请求 (Request)</div>
|
||||
<div class="box-body">
|
||||
<div class="line method">GET /index.html HTTP/1.1</div>
|
||||
<div class="line header">Host: www.example.com</div>
|
||||
<div class="line header">User-Agent: Mozilla/5.0</div>
|
||||
<div class="line header">Accept: text/html</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="arrow">→</div>
|
||||
|
||||
<div class="response-box">
|
||||
<div class="box-header">📥 响应 (Response)</div>
|
||||
<div class="box-body">
|
||||
<div class="line status">HTTP/1.1 200 OK</div>
|
||||
<div class="line header">Content-Type: text/html</div>
|
||||
<div class="line header">Content-Length: 1234</div>
|
||||
<div class="line empty"></div>
|
||||
<div class="line body"><html>...</html></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- DNS 查询示例 -->
|
||||
<div v-if="activeProtocol === 'dns'" class="dns-example">
|
||||
<div class="example-title">DNS 查询过程</div>
|
||||
<div class="dns-flow">
|
||||
<div class="flow-step">
|
||||
<div class="step-icon">💻</div>
|
||||
<div class="step-text">用户输入 www.example.com</div>
|
||||
</div>
|
||||
<div class="flow-arrow">→</div>
|
||||
<div class="flow-step">
|
||||
<div class="step-icon">🔍</div>
|
||||
<div class="step-text">DNS 服务器查询</div>
|
||||
</div>
|
||||
<div class="flow-arrow">→</div>
|
||||
<div class="flow-step">
|
||||
<div class="step-icon">📍</div>
|
||||
<div class="step-text">返回 IP: 93.184.216.34</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const activeProtocol = ref('http')
|
||||
|
||||
const protocols = [
|
||||
{
|
||||
id: 'http',
|
||||
name: 'HTTP',
|
||||
icon: '🌐',
|
||||
desc: '网页浏览的基础'
|
||||
},
|
||||
{
|
||||
id: 'https',
|
||||
name: 'HTTPS',
|
||||
icon: '🔐',
|
||||
desc: '加密的安全连接'
|
||||
},
|
||||
{
|
||||
id: 'dns',
|
||||
name: 'DNS',
|
||||
icon: '🔍',
|
||||
desc: '域名解析服务'
|
||||
},
|
||||
{
|
||||
id: 'dhcp',
|
||||
name: 'DHCP',
|
||||
icon: '📡',
|
||||
desc: '自动分配 IP 地址'
|
||||
},
|
||||
{
|
||||
id: 'smtp',
|
||||
name: 'SMTP',
|
||||
icon: '📧',
|
||||
desc: '发送邮件'
|
||||
},
|
||||
{
|
||||
id: 'ftp',
|
||||
name: 'FTP',
|
||||
icon: '📁',
|
||||
desc: '文件传输'
|
||||
}
|
||||
]
|
||||
|
||||
const protocolDetails = {
|
||||
http: {
|
||||
name: 'HTTP',
|
||||
icon: '🌐',
|
||||
purpose: '超文本传输协议,用于在浏览器和服务器之间传输网页数据',
|
||||
steps: [
|
||||
'浏览器发起 HTTP 请求',
|
||||
'服务器接收并处理请求',
|
||||
'服务器返回 HTTP 响应',
|
||||
'浏览器解析并显示网页'
|
||||
],
|
||||
apps: [
|
||||
{ icon: '🌍', name: '网页浏览' },
|
||||
{ icon: '📱', name: '移动应用 API' },
|
||||
{ icon: '🔌', name: 'RESTful 服务' }
|
||||
]
|
||||
},
|
||||
https: {
|
||||
name: 'HTTPS',
|
||||
icon: '🔐',
|
||||
purpose: 'HTTP Secure,在 HTTP 基础上加入 SSL/TLS 加密层',
|
||||
steps: [
|
||||
'客户端请求 HTTPS 连接',
|
||||
'服务器发送数字证书',
|
||||
'客户端验证证书并生成会话密钥',
|
||||
'使用加密通道传输数据'
|
||||
],
|
||||
apps: [
|
||||
{ icon: '🏦', name: '网上银行' },
|
||||
{ icon: '🛒', name: '在线支付' },
|
||||
{ icon: '🔑', name: '登录认证' }
|
||||
]
|
||||
},
|
||||
dns: {
|
||||
name: 'DNS',
|
||||
icon: '🔍',
|
||||
purpose: '域名系统,将人类可读的域名转换为机器可读的 IP 地址',
|
||||
steps: [
|
||||
'用户输入域名',
|
||||
'查询本地 DNS 缓存',
|
||||
'若缓存未命中,查询 DNS 服务器',
|
||||
'返回对应的 IP 地址'
|
||||
],
|
||||
apps: [
|
||||
{ icon: '🌐', name: '网址访问' },
|
||||
{ icon: '📧', name: '邮件服务器' },
|
||||
{ icon: '🎮', name: '游戏连接' }
|
||||
]
|
||||
},
|
||||
dhcp: {
|
||||
name: 'DHCP',
|
||||
icon: '📡',
|
||||
purpose: '动态主机配置协议,自动为设备分配 IP 地址和网络配置',
|
||||
steps: [
|
||||
'设备发送 DHCP Discover',
|
||||
'DHCP 服务器发送 Offer',
|
||||
'设备发送 Request',
|
||||
'服务器发送 ACK,完成分配'
|
||||
],
|
||||
apps: [
|
||||
{ icon: '📱', name: '手机连 WiFi' },
|
||||
{ icon: '💻', name: '电脑入网' },
|
||||
{ icon: '🏠', name: '家庭网络' }
|
||||
]
|
||||
},
|
||||
smtp: {
|
||||
name: 'SMTP',
|
||||
icon: '📧',
|
||||
purpose: '简单邮件传输协议,用于发送电子邮件',
|
||||
steps: [
|
||||
'邮件客户端连接 SMTP 服务器',
|
||||
'验证发件人身份',
|
||||
'传输邮件内容和附件',
|
||||
'服务器将邮件投递到收件人服务器'
|
||||
],
|
||||
apps: [
|
||||
{ icon: '📬', name: '邮件发送' },
|
||||
{ icon: '🔔', name: '邮件通知' },
|
||||
{ icon: '📋', name: '邮件列表' }
|
||||
]
|
||||
},
|
||||
ftp: {
|
||||
name: 'FTP',
|
||||
icon: '📁',
|
||||
purpose: '文件传输协议,用于在网络上进行文件传输',
|
||||
steps: [
|
||||
'客户端建立 FTP 控制连接',
|
||||
'用户认证(用户名密码)',
|
||||
'建立数据连接传输文件',
|
||||
'传输完成后关闭连接'
|
||||
],
|
||||
apps: [
|
||||
{ icon: '⬆️', name: '文件上传' },
|
||||
{ icon: '⬇️', name: '文件下载' },
|
||||
{ icon: '📂', name: '文件管理' }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
const currentProtocol = computed(() => protocolDetails[activeProtocol.value])
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.application-layer-demo {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.demo-header .icon { font-size: 1.5rem; }
|
||||
.demo-header .title { font-weight: 700; font-size: 1.1rem; }
|
||||
.demo-header .subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
|
||||
|
||||
.protocol-gallery {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
gap: 1rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.protocol-card {
|
||||
text-align: center;
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.protocol-card:hover {
|
||||
border-color: var(--vp-c-brand);
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
|
||||
.protocol-card.active {
|
||||
border-color: var(--vp-c-brand);
|
||||
background: var(--vp-c-brand-soft);
|
||||
}
|
||||
|
||||
.card-icon {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.card-name {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.card-desc {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.protocol-detail {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.detail-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 1.5rem;
|
||||
padding-bottom: 1rem;
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.detail-icon {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.detail-title {
|
||||
font-weight: 600;
|
||||
font-size: 1.1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.detail-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.detail-section {
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 0.75rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.section-text {
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.6;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.section-steps {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.step-item {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.step-num {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.step-text {
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.5;
|
||||
padding-top: 0.15rem;
|
||||
}
|
||||
|
||||
.app-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.app-tag {
|
||||
padding: 0.5rem 0.75rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 20px;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.http-example,
|
||||
.dns-example {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.example-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.request-response {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.request-box,
|
||||
.response-box {
|
||||
flex: 1;
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.box-header {
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
padding: 0.5rem;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.box-body {
|
||||
padding: 0.75rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.line {
|
||||
padding: 0.25rem 0;
|
||||
}
|
||||
|
||||
.line.method {
|
||||
color: var(--vp-c-brand);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.line.status {
|
||||
color: #10b981;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.line.header {
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.line.body {
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.arrow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 2rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.dns-flow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.flow-step {
|
||||
flex: 1;
|
||||
min-width: 150px;
|
||||
text-align: center;
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.step-icon {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.step-text {
|
||||
font-size: 0.85rem;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.flow-arrow {
|
||||
font-size: 1.5rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.request-response {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
+327
@@ -0,0 +1,327 @@
|
||||
<template>
|
||||
<div class="compilation-practice-demo">
|
||||
<div class="demo-header">
|
||||
<span class="icon">⚙️</span>
|
||||
<span class="title">编译过程实践</span>
|
||||
<span class="subtitle">从代码到可执行文件</span>
|
||||
</div>
|
||||
|
||||
<div class="code-input">
|
||||
<div class="input-title">输入代码</div>
|
||||
<textarea
|
||||
v-model="sourceCode"
|
||||
class="code-textarea"
|
||||
placeholder="输入 C 语言代码..."
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<div class="compilation-steps">
|
||||
<div class="steps-title">编译步骤</div>
|
||||
<div class="steps-flow">
|
||||
<div
|
||||
v-for="(step, index) in steps"
|
||||
:key="index"
|
||||
class="step-item"
|
||||
>
|
||||
<div class="step-number">{{ index + 1 }}</div>
|
||||
<div class="step-content">
|
||||
<div class="step-name">{{ step.name }}</div>
|
||||
<div class="step-command">{{ step.command }}</div>
|
||||
<div class="step-output">{{ step.output }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="file-outputs">
|
||||
<div class="outputs-title">生成的文件</div>
|
||||
<div class="file-list">
|
||||
<div
|
||||
v-for="file in outputFiles"
|
||||
:key="file.name"
|
||||
class="file-item"
|
||||
>
|
||||
<div class="file-icon">{{ file.icon }}</div>
|
||||
<div class="file-info">
|
||||
<div class="file-name">{{ file.name }}</div>
|
||||
<div class="file-desc">{{ file.desc }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tools">
|
||||
<div class="tools-title">常用编译工具</div>
|
||||
<div class="tools-grid">
|
||||
<div class="tool-card">
|
||||
<div class="tool-name">GCC</div>
|
||||
<div class="tool-desc">GNU Compiler Collection</div>
|
||||
</div>
|
||||
<div class="tool-card">
|
||||
<div class="tool-name">Clang</div>
|
||||
<div class="tool-desc">LLVM 的 C/C++ 编译器</div>
|
||||
</div>
|
||||
<div class="tool-card">
|
||||
<div class="tool-name">MSVC</div>
|
||||
<div class="tool-desc">Microsoft Visual C++</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const sourceCode = ref(`#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
printf("Hello, World!\\n");
|
||||
return 0;
|
||||
}`)
|
||||
|
||||
const steps = [
|
||||
{
|
||||
name: '预处理',
|
||||
command: 'gcc -E hello.c -o hello.i',
|
||||
output: '处理 #include,展开宏定义'
|
||||
},
|
||||
{
|
||||
name: '编译',
|
||||
command: 'gcc -S hello.i -o hello.s',
|
||||
output: '生成汇编代码'
|
||||
},
|
||||
{
|
||||
name: '汇编',
|
||||
command: 'gcc -c hello.s -o hello.o',
|
||||
output: '生成目标文件'
|
||||
},
|
||||
{
|
||||
name: '链接',
|
||||
command: 'gcc hello.o -o hello',
|
||||
output: '生成可执行文件'
|
||||
}
|
||||
]
|
||||
|
||||
const outputFiles = [
|
||||
{
|
||||
name: 'hello.c',
|
||||
icon: '📄',
|
||||
desc: '源代码文件'
|
||||
},
|
||||
{
|
||||
name: 'hello.i',
|
||||
icon: '📝',
|
||||
desc: '预处理后的文件'
|
||||
},
|
||||
{
|
||||
name: 'hello.s',
|
||||
icon: '⚙️',
|
||||
desc: '汇编代码文件'
|
||||
},
|
||||
{
|
||||
name: 'hello.o',
|
||||
icon: '📦',
|
||||
desc: '目标文件'
|
||||
},
|
||||
{
|
||||
name: 'hello',
|
||||
icon: '🚀',
|
||||
desc: '可执行文件'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.compilation-practice-demo {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.demo-header .icon { font-size: 1.5rem; }
|
||||
.demo-header .title { font-weight: 700; font-size: 1.1rem; }
|
||||
.demo-header .subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
|
||||
|
||||
.code-input {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.input-title {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 0.75rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.code-textarea {
|
||||
width: 100%;
|
||||
min-height: 150px;
|
||||
padding: 1rem;
|
||||
background: #1e1e1e;
|
||||
color: #d4d4d4;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.6;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.compilation-steps {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.steps-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.steps-flow {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.step-item {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.step-number {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.step-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.step-name {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.step-command {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-brand);
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.step-output {
|
||||
font-size: 0.85rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.file-outputs {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.outputs-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.file-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.file-item {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
padding: 0.75rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.file-icon {
|
||||
font-size: 1.5rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.file-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.file-name {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.file-desc {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.tools {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.tools-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.tools-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.tool-card {
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 6px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tool-name {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 0.5rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.tool-desc {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
</style>
|
||||
+529
@@ -0,0 +1,529 @@
|
||||
<template>
|
||||
<div class="compiler-analogy-demo">
|
||||
<div class="demo-header">
|
||||
<span class="icon">🌐</span>
|
||||
<span class="title">编译原理:翻译的艺术</span>
|
||||
<span class="subtitle">如何把代码翻译成机器指令</span>
|
||||
</div>
|
||||
|
||||
<div class="analogy-intro">
|
||||
<div class="analogy-box">
|
||||
<div class="analogy-text">
|
||||
编译器就像<strong>翻译官</strong>,把人类能懂的代码翻译成机器能懂的指令
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 翻译过程 -->
|
||||
<div class="translation-process">
|
||||
<div class="process-title">代码翻译的完整流程</div>
|
||||
<div class="process-flow">
|
||||
<div
|
||||
v-for="(step, index) in translationSteps"
|
||||
:key="index"
|
||||
class="process-step"
|
||||
>
|
||||
<div class="step-number">{{ index + 1 }}</div>
|
||||
<div class="step-content">
|
||||
<div class="step-name">{{ step.name }}</div>
|
||||
<div class="step-desc">{{ step.desc }}</div>
|
||||
<div class="step-example">{{ step.example }}</div>
|
||||
</div>
|
||||
<div v-if="index < translationSteps.length - 1" class="step-arrow">→</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 词法分析 -->
|
||||
<div class="analyzer-section">
|
||||
<div class="analyzer-title">词法分析:分词</div>
|
||||
<div class="lexical-demo">
|
||||
<div class="source-code">
|
||||
<code>int age = 25;</code>
|
||||
</div>
|
||||
<div class="token-arrow">↓</div>
|
||||
<div class="tokens-list">
|
||||
<div
|
||||
v-for="(token, index) in tokens"
|
||||
:key="index"
|
||||
class="token-item"
|
||||
>
|
||||
<span class="token-type">{{ token.type }}</span>
|
||||
<span class="token-value">{{ token.value }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 语法分析 -->
|
||||
<div class="analyzer-section">
|
||||
<div class="analyzer-title">语法分析:构建树</div>
|
||||
<div class="syntax-demo">
|
||||
<div class="syntax-tree">
|
||||
<div class="tree-node root">
|
||||
<span class="node-label">赋值语句</span>
|
||||
<div class="node-children">
|
||||
<div class="tree-node">
|
||||
<span class="node-label">变量</span>
|
||||
<span class="node-value">age</span>
|
||||
</div>
|
||||
<div class="tree-node">
|
||||
<span class="node-label">运算符</span>
|
||||
<span class="node-value">=</span>
|
||||
</div>
|
||||
<div class="tree-node">
|
||||
<span class="node-label">数字</span>
|
||||
<span class="node-value">25</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 编译 vs 解释 -->
|
||||
<div class="comparison">
|
||||
<div class="comparison-title">编译 vs 解释</div>
|
||||
<div class="comparison-box">
|
||||
<div class="compare-side compile">
|
||||
<div class="side-header">编译型语言</div>
|
||||
<div class="side-content">
|
||||
<div class="side-step">源代码 → 编译器 → 机器码</div>
|
||||
<div class="side-example">C, Go, Rust</div>
|
||||
<div class="side-features">
|
||||
<div class="feature">✓ 执行快</div>
|
||||
<div class="feature">✓ 一次编译多次运行</div>
|
||||
<div class="feature">✗ 编译慢</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="compare-side interpret">
|
||||
<div class="side-header">解释型语言</div>
|
||||
<div class="side-content">
|
||||
<div class="side-step">源代码 → 解释器 → 逐行执行</div>
|
||||
<div class="side-example">Python, JavaScript, PHP</div>
|
||||
<div class="side-features">
|
||||
<div class="feature">✓ 开发快</div>
|
||||
<div class="feature">✓ 跨平台</div>
|
||||
<div class="feature">✗ 执行慢</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 优化 -->
|
||||
<div class="optimization">
|
||||
<div class="optimization-title">编译器优化</div>
|
||||
<div class="optimization-content">
|
||||
<div class="opt-examples">
|
||||
<div class="opt-item">
|
||||
<div class="opt-before">优化前:</div>
|
||||
<div class="opt-code">x = 5 + 3 + 2</div>
|
||||
</div>
|
||||
<div class="opt-arrow">⬇️</div>
|
||||
<div class="opt-item">
|
||||
<div class="opt-after">优化后:</div>
|
||||
<div class="opt-code">x = 10</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="opt-note">
|
||||
编译器会自动优化代码,提高运行效率
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const translationSteps = [
|
||||
{
|
||||
name: '词法分析',
|
||||
desc: '将代码分解成一个个单词(token)',
|
||||
example: 'int age = 25 → [int, age, =, 25]'
|
||||
},
|
||||
{
|
||||
name: '语法分析',
|
||||
desc: '检查代码是否符合语法规则,构建语法树',
|
||||
example: '验证语句结构是否正确'
|
||||
},
|
||||
{
|
||||
name: '语义分析',
|
||||
desc: '检查代码的含义是否合理',
|
||||
example: '检查变量是否定义、类型是否匹配'
|
||||
},
|
||||
{
|
||||
name: '中间代码生成',
|
||||
desc: '生成与机器无关的中间表示',
|
||||
example: '生成字节码或中间表示'
|
||||
},
|
||||
{
|
||||
name: '优化',
|
||||
desc: '改进代码,提高执行效率',
|
||||
example: '常量折叠、死代码消除'
|
||||
},
|
||||
{
|
||||
name: '目标代码生成',
|
||||
desc: '生成机器码或目标代码',
|
||||
example: '生成 x86、ARM 等机器指令'
|
||||
}
|
||||
]
|
||||
|
||||
const tokens = [
|
||||
{ type: '关键字', value: 'int' },
|
||||
{ type: '标识符', value: 'age' },
|
||||
{ type: '运算符', value: '=' },
|
||||
{ type: '数字', value: '25' },
|
||||
{ type: '分隔符', value: ';' }
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.compiler-analogy-demo {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.demo-header .icon { font-size: 1.5rem; }
|
||||
.demo-header .title { font-weight: 700; font-size: 1.1rem; }
|
||||
.demo-header .subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
|
||||
|
||||
.analogy-intro {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.analogy-box {
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg);
|
||||
border-left: 4px solid var(--vp-c-brand);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.analogy-text {
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.translation-process {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.process-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
text-align: center;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.process-flow {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.process-step {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.step-number {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.step-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.step-name {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 0.35rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.step-desc {
|
||||
font-size: 0.85rem;
|
||||
color: var(--vp-c-text-2);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.step-example {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-1);
|
||||
background: var(--vp-c-bg-soft);
|
||||
padding: 0.5rem;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.step-arrow {
|
||||
font-size: 1.5rem;
|
||||
color: var(--vp-c-brand);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.analyzer-section {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.analyzer-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.lexical-demo {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.source-code {
|
||||
padding: 1rem;
|
||||
background: #1e1e1e;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.source-code code {
|
||||
color: #d4d4d4;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.token-arrow {
|
||||
font-size: 1.5rem;
|
||||
color: var(--vp-c-brand);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.tokens-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.token-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.35rem;
|
||||
padding: 0.75rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
.token-type {
|
||||
font-size: 0.75rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.token-value {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.syntax-demo {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.syntax-tree {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.tree-node {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
padding: 0.75rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.tree-node.root {
|
||||
background: var(--vp-c-brand-soft);
|
||||
border-color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.node-label {
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.node-value {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.node-children {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
.comparison {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.comparison-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.comparison-box {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.comparison-box {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.compare-side {
|
||||
padding: 1.5rem;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.side-header {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.compile .side-header {
|
||||
color: #10b981;
|
||||
}
|
||||
|
||||
.interpret .side-header {
|
||||
color: #3b82f6;
|
||||
}
|
||||
|
||||
.side-step {
|
||||
text-align: center;
|
||||
padding: 0.75rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 6px;
|
||||
margin-bottom: 1rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.side-example {
|
||||
text-align: center;
|
||||
margin-bottom: 1rem;
|
||||
font-size: 0.85rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.side-features {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.feature {
|
||||
font-size: 0.85rem;
|
||||
padding: 0.5rem;
|
||||
background: var(--vp-c-bg);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.optimization {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.optimization-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.optimization-content {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.opt-examples {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.opt-item {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.opt-before,
|
||||
.opt-after {
|
||||
font-size: 0.85rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.opt-code {
|
||||
font-family: 'Courier New', monospace;
|
||||
padding: 0.75rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 6px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.opt-arrow {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.opt-note {
|
||||
font-size: 0.85rem;
|
||||
color: var(--vp-c-text-2);
|
||||
font-style: italic;
|
||||
}
|
||||
</style>
|
||||
+263
@@ -0,0 +1,263 @@
|
||||
<template>
|
||||
<div class="data-encoding-basics-demo">
|
||||
<div class="demo-header">
|
||||
<span class="icon">🔤</span>
|
||||
<span class="title">数据编码基础</span>
|
||||
<span class="subtitle">信息如何被表示和存储</span>
|
||||
</div>
|
||||
|
||||
<div class="encoding-intro">
|
||||
计算机只能识别 <strong>0 和 1</strong>,所有数据都需要转换成二进制
|
||||
</div>
|
||||
|
||||
<div class="bit-byte">
|
||||
<div class="bb-cards">
|
||||
<div class="bb-card">
|
||||
<div class="bb-title">位 (Bit)</div>
|
||||
<div class="bb-value">0 或 1</div>
|
||||
<div class="bb-desc">最小数据单位</div>
|
||||
</div>
|
||||
<div class="bb-card">
|
||||
<div class="bb-title">字节 (Byte)</div>
|
||||
<div class="bb-value">8 位</div>
|
||||
<div class="bb-desc">常用存储单位</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="encoding-examples">
|
||||
<div class="example-title">不同数据的编码方式</div>
|
||||
<div class="example-grid">
|
||||
<div class="example-card">
|
||||
<div class="card-icon">🔢</div>
|
||||
<div class="card-title">数字</div>
|
||||
<div class="card-encoding">
|
||||
<div class="encoding-label">十进制</div>
|
||||
<div class="encoding-value">25</div>
|
||||
</div>
|
||||
<div class="card-encoding">
|
||||
<div class="encoding-label">二进制</div>
|
||||
<div class="encoding-value">00011001</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="example-card">
|
||||
<div class="card-icon">🔤</div>
|
||||
<div class="card-title">字符</div>
|
||||
<div class="card-encoding">
|
||||
<div class="encoding-label">字符</div>
|
||||
<div class="encoding-value">A</div>
|
||||
</div>
|
||||
<div class="card-encoding">
|
||||
<div class="encoding-label">ASCII</div>
|
||||
<div class="encoding-value">01000001</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="example-card">
|
||||
<div class="card-icon">🎨</div>
|
||||
<div class="card-title">颜色</div>
|
||||
<div class="card-encoding">
|
||||
<div class="encoding-label">RGB</div>
|
||||
<div class="encoding-value">255,0,0</div>
|
||||
</div>
|
||||
<div class="card-encoding">
|
||||
<div class="encoding-label">十六进制</div>
|
||||
<div class="encoding-value">#FF0000</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="encoding-standards">
|
||||
<div class="standards-title">常见编码标准</div>
|
||||
<table class="standards-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>编码</th>
|
||||
<th>说明</th>
|
||||
<th>用途</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>ASCII</td>
|
||||
<td>7 位,128 个字符</td>
|
||||
<td>英文字符</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Unicode</td>
|
||||
<td>统一码,全球字符</td>
|
||||
<td>多语言文本</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>UTF-8</td>
|
||||
<td>变长编码,1-4 字节</td>
|
||||
<td>网页文本</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Base64</td>
|
||||
<td>二进制转文本</td>
|
||||
<td>邮件、图片</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.data-encoding-basics-demo {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.demo-header .icon { font-size: 1.5rem; }
|
||||
.demo-header .title { font-weight: 700; font-size: 1.1rem; }
|
||||
.demo-header .subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
|
||||
|
||||
.encoding-intro {
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg);
|
||||
border-left: 4px solid var(--vp-c-brand);
|
||||
border-radius: 6px;
|
||||
margin-bottom: 2rem;
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.bit-byte {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.bb-cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.bb-card {
|
||||
padding: 1.5rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.bb-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.bb-value {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 1.2rem;
|
||||
color: var(--vp-c-brand);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.bb-desc {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.encoding-examples {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.example-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
text-align: center;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.example-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.example-card {
|
||||
padding: 1.25rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.card-icon {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.card-encoding {
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.encoding-label {
|
||||
font-size: 0.75rem;
|
||||
color: var(--vp-c-text-2);
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.encoding-value {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 0.9rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.encoding-standards {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.standards-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.standards-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.standards-table th {
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
padding: 0.75rem;
|
||||
text-align: left;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.standards-table td {
|
||||
padding: 0.75rem;
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
</style>
|
||||
+401
@@ -0,0 +1,401 @@
|
||||
<template>
|
||||
<div class="data-lifecycle-demo">
|
||||
<div class="demo-header">
|
||||
<span class="icon">🔄</span>
|
||||
<span class="title">数据的生命周期</span>
|
||||
<span class="subtitle">从输入到存储到传输到输出的全过程</span>
|
||||
</div>
|
||||
|
||||
<div class="lifecycle-flow">
|
||||
<div class="flow-stage" v-for="(stage, index) in stages" :key="stage.id">
|
||||
<div class="stage-header" @click="activeStage = index">
|
||||
<span class="stage-number">{{ index + 1 }}</span>
|
||||
<span class="stage-name">{{ stage.name }}</span>
|
||||
<span class="stage-icon">{{ stage.icon }}</span>
|
||||
</div>
|
||||
|
||||
<Transition name="slide">
|
||||
<div v-if="activeStage === index" class="stage-detail">
|
||||
<div class="detail-content">
|
||||
<h4>{{ stage.title }}</h4>
|
||||
<p>{{ stage.description }}</p>
|
||||
|
||||
<div class="stage-example">
|
||||
<div class="example-label">示例:{{ stage.example.label }}</div>
|
||||
<div class="example-content">
|
||||
<div v-for="(item, i) in stage.example.items" :key="i" class="example-item">
|
||||
<span class="item-label">{{ item.label }}:</span>
|
||||
<span class="item-value">{{ item.value }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stage-encoding">
|
||||
<div class="encoding-label">编码方式:</div>
|
||||
<div class="encoding-value">{{ stage.encoding }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<div v-if="index < stages.length - 1" class="flow-arrow">↓</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="lifecycle-summary">
|
||||
<div class="summary-title">数据转换的关键点</div>
|
||||
<div class="summary-grid">
|
||||
<div v-for="(point, index) in keyPoints" :key="index" class="summary-card">
|
||||
<div class="card-icon">{{ point.icon }}</div>
|
||||
<div class="card-text">
|
||||
<div class="card-title">{{ point.title }}</div>
|
||||
<div class="card-desc">{{ point.desc }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const activeStage = ref(0)
|
||||
|
||||
const stages = [
|
||||
{
|
||||
id: 'input',
|
||||
name: '数据输入',
|
||||
icon: '⌨️',
|
||||
title: '阶段 1:数据输入',
|
||||
description: '用户通过各种输入设备(键盘、鼠标、触摸屏、麦克风等)将信息输入到计算机系统中。',
|
||||
example: {
|
||||
label: '用户输入文字',
|
||||
items: [
|
||||
{ label: '原始动作', value: '按下键盘 A 键' },
|
||||
{ label: '硬件信号', value: '键盘扫描码' },
|
||||
{ label: '操作系统', value: '键盘中断' }
|
||||
]
|
||||
},
|
||||
encoding: 'ASCII: 01000001 (65)'
|
||||
},
|
||||
{
|
||||
id: 'processing',
|
||||
name: '数据处理',
|
||||
icon: '🔄',
|
||||
title: '阶段 2:数据处理',
|
||||
description: 'CPU 对输入的数据进行计算、转换、格式化等操作,应用程序根据业务逻辑处理数据。',
|
||||
example: {
|
||||
label: '文本编辑器处理',
|
||||
items: [
|
||||
{ label: '应用程序', value: '接收字符 "A"' },
|
||||
{ label: '内存存储', value: 'Unicode: U+0041' },
|
||||
{ label: '显示准备', value: '字体渲染' }
|
||||
]
|
||||
},
|
||||
encoding: 'UTF-8: 0x41 (单字节)'
|
||||
},
|
||||
{
|
||||
id: 'storage',
|
||||
name: '数据存储',
|
||||
icon: '💾',
|
||||
title: '阶段 3:数据存储',
|
||||
description: '处理后的数据被保存到存储设备中(内存、硬盘、SSD、云存储等),以便后续使用。',
|
||||
example: {
|
||||
label: '保存文档',
|
||||
items: [
|
||||
{ label: '内存数据', value: '文本内容' },
|
||||
{ label: '文件系统', value: '创建 .txt 文件' },
|
||||
{ label: '磁盘写入', value: '二进制数据' }
|
||||
]
|
||||
},
|
||||
encoding: '磁盘: 二进制位序列'
|
||||
},
|
||||
{
|
||||
id: 'transmission',
|
||||
name: '数据传输',
|
||||
icon: '📡',
|
||||
title: '阶段 4:数据传输',
|
||||
description: '数据通过网络(局域网、互联网)或内部总线从一个位置传输到另一个位置。',
|
||||
example: {
|
||||
label: '上传文件',
|
||||
items: [
|
||||
{ label: '文件读取', value: '从磁盘加载' },
|
||||
{ label: '网络封装', value: 'TCP/IP 数据包' },
|
||||
{ label: '物理传输', value: '电信号/光信号' }
|
||||
]
|
||||
},
|
||||
encoding: '网络: 数据包帧格式'
|
||||
},
|
||||
{
|
||||
id: 'output',
|
||||
name: '数据输出',
|
||||
icon: '🖥️',
|
||||
title: '阶段 5:数据输出',
|
||||
description: '数据通过输出设备(显示器、打印机、扬声器等)呈现给用户,或传输给其他系统。',
|
||||
example: {
|
||||
label: '显示网页',
|
||||
items: [
|
||||
{ label: '浏览器接收', value: 'HTML 数据' },
|
||||
{ label: '渲染引擎', value: '解析样式、布局' },
|
||||
{ label: '屏幕显示', value: '像素点阵' }
|
||||
]
|
||||
},
|
||||
encoding: '显示: RGB 像素值'
|
||||
}
|
||||
]
|
||||
|
||||
const keyPoints = [
|
||||
{
|
||||
icon: '🔤',
|
||||
title: '编码转换',
|
||||
desc: '数据在不同阶段使用不同的编码方式(ASCII、Unicode、二进制等)'
|
||||
},
|
||||
{
|
||||
icon: '📦',
|
||||
title: '封装格式',
|
||||
desc: '传输和存储时需要封装成特定格式(文件、数据包、帧等)'
|
||||
},
|
||||
{
|
||||
icon: '🎯',
|
||||
title: '协议标准',
|
||||
desc: '每个环节都遵循相应的协议和标准(TCP/IP、USB、HDMI等)'
|
||||
},
|
||||
{
|
||||
icon: '⚡',
|
||||
title: '性能优化',
|
||||
desc: '编码压缩、缓存、流水线等技术提升数据处理效率'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.data-lifecycle-demo {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.demo-header .icon { font-size: 1.5rem; }
|
||||
.demo-header .title { font-weight: 700; font-size: 1.1rem; }
|
||||
.demo-header .subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
|
||||
|
||||
.lifecycle-flow {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.flow-stage {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.stage-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 1rem 1.5rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
width: 100%;
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
.stage-header:hover {
|
||||
border-color: var(--vp-c-brand);
|
||||
transform: translateX(5px);
|
||||
}
|
||||
|
||||
.stage-number {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.stage-name {
|
||||
flex: 1;
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.stage-icon {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.flow-arrow {
|
||||
font-size: 1.5rem;
|
||||
color: var(--vp-c-text-2);
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.stage-detail {
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.detail-content {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
.detail-content h4 {
|
||||
margin: 0 0 0.75rem 0;
|
||||
color: var(--vp-c-brand);
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.detail-content > p {
|
||||
margin: 0 0 1rem 0;
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.6;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.stage-example {
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 6px;
|
||||
padding: 0.75rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.example-label {
|
||||
font-weight: 600;
|
||||
font-size: 0.85rem;
|
||||
color: var(--vp-c-brand);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.example-item {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.85rem;
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.example-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.item-label {
|
||||
color: var(--vp-c-text-2);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.item-value {
|
||||
color: var(--vp-c-text-1);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.stage-encoding {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
align-items: center;
|
||||
font-size: 0.85rem;
|
||||
padding: 0.5rem 0.75rem;
|
||||
background: var(--vp-c-brand-soft);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.encoding-label {
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.encoding-value {
|
||||
font-family: 'Courier New', monospace;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.lifecycle-summary {
|
||||
border-top: 1px solid var(--vp-c-divider);
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
|
||||
.summary-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.summary-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.summary-card {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
padding: 0.75rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.card-icon {
|
||||
font-size: 1.5rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.card-text {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.card-desc {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.slide-enter-active,
|
||||
.slide-leave-active {
|
||||
transition: all 0.3s ease;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.slide-enter-from,
|
||||
.slide-leave-to {
|
||||
opacity: 0;
|
||||
max-height: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
|
||||
.slide-enter-to,
|
||||
.slide-leave-from {
|
||||
opacity: 1;
|
||||
max-height: 500px;
|
||||
transform: translateY(0);
|
||||
}
|
||||
</style>
|
||||
+480
@@ -0,0 +1,480 @@
|
||||
<template>
|
||||
<div class="data-link-demo">
|
||||
<div class="demo-header">
|
||||
<span class="icon">🔗</span>
|
||||
<span class="title">数据链路层:帧的传递</span>
|
||||
<span class="subtitle">MAC 地址如何定位设备</span>
|
||||
</div>
|
||||
|
||||
<div class="lan-scene">
|
||||
<div class="lan-title">局域网场景</div>
|
||||
<div class="lan-devices">
|
||||
<div
|
||||
v-for="device in devices"
|
||||
:key="device.id"
|
||||
:class="['lan-device', { active: activeDevice === device.id, sender: device.role === 'sender', receiver: device.role === 'receiver' }]"
|
||||
@click="activeDevice = device.id"
|
||||
>
|
||||
<div class="device-icon">{{ device.icon }}</div>
|
||||
<div class="device-name">{{ device.name }}</div>
|
||||
<div class="device-mac">{{ device.mac }}</div>
|
||||
<div v-if="device.role" class="device-role">{{ device.roleText }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 交换机 -->
|
||||
<div class="switch">
|
||||
<div class="switch-icon">🔄</div>
|
||||
<div class="switch-name">交换机</div>
|
||||
<div class="switch-desc">根据 MAC 地址转发数据帧</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 帧结构 -->
|
||||
<div class="frame-structure">
|
||||
<div class="frame-title">以太网帧结构</div>
|
||||
<div class="frame-visual">
|
||||
<div class="frame-fields">
|
||||
<div
|
||||
v-for="(field, index) in frameFields"
|
||||
:key="index"
|
||||
:class="['frame-field', { highlighted: activeDevice !== null }]"
|
||||
:style="{ width: field.width }"
|
||||
>
|
||||
<div class="field-name">{{ field.name }}</div>
|
||||
<div class="field-value">{{ field.value }}</div>
|
||||
<div class="field-size">{{ field.size }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 传输过程 -->
|
||||
<div class="transfer-process">
|
||||
<div class="process-title">数据帧传输过程</div>
|
||||
<div class="process-steps">
|
||||
<div
|
||||
v-for="(step, index) in transferSteps"
|
||||
:key="index"
|
||||
:class="['process-step', { active: activeStep === index }]"
|
||||
>
|
||||
<div class="step-number">{{ index + 1 }}</div>
|
||||
<div class="step-content">
|
||||
<div class="step-title">{{ step.title }}</div>
|
||||
<div class="step-desc">{{ step.desc }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ARP 协议 -->
|
||||
<div class="arp-section">
|
||||
<div class="arp-title">ARP:IP 地址到 MAC 地址的映射</div>
|
||||
<div class="arp-example">
|
||||
<div class="arp-question">
|
||||
<span class="question-icon">❓</span>
|
||||
<span class="question-text">谁有 IP 地址 192.168.1.200?</span>
|
||||
</div>
|
||||
<div class="arp-arrow">↓ 广播到局域网</div>
|
||||
<div class="arp-answer">
|
||||
<span class="answer-icon">✅</span>
|
||||
<span class="answer-text">我是!我的 MAC 地址是 00:11:22:33:44:66</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const activeDevice = ref(null)
|
||||
const activeStep = ref(0)
|
||||
|
||||
const devices = [
|
||||
{
|
||||
id: 'pc1',
|
||||
name: '电脑 A',
|
||||
icon: '💻',
|
||||
mac: '00:11:22:33:44:55',
|
||||
ip: '192.168.1.100',
|
||||
role: 'sender',
|
||||
roleText: '发送方'
|
||||
},
|
||||
{
|
||||
id: 'pc2',
|
||||
name: '电脑 B',
|
||||
icon: '🖥️',
|
||||
mac: '00:11:22:33:44:66',
|
||||
ip: '192.168.1.200',
|
||||
role: 'receiver',
|
||||
roleText: '接收方'
|
||||
},
|
||||
{
|
||||
id: 'printer',
|
||||
name: '打印机',
|
||||
icon: '🖨️',
|
||||
mac: '00:11:22:33:44:77',
|
||||
ip: '192.168.1.50'
|
||||
},
|
||||
{
|
||||
id: 'phone',
|
||||
name: '手机',
|
||||
icon: '📱',
|
||||
mac: '00:11:22:33:44:88',
|
||||
ip: '192.168.1.150'
|
||||
}
|
||||
]
|
||||
|
||||
const frameFields = [
|
||||
{
|
||||
name: '目的 MAC',
|
||||
value: '00:11:22:33:44:66',
|
||||
size: '6 字节',
|
||||
width: '18%'
|
||||
},
|
||||
{
|
||||
name: '源 MAC',
|
||||
value: '00:11:22:33:44:55',
|
||||
size: '6 字节',
|
||||
width: '18%'
|
||||
},
|
||||
{
|
||||
name: '类型',
|
||||
value: '0x0800 (IPv4)',
|
||||
size: '2 字节',
|
||||
width: '12%'
|
||||
},
|
||||
{
|
||||
name: '数据',
|
||||
value: 'IP 数据包...',
|
||||
size: '46-1500 字节',
|
||||
width: '44%'
|
||||
},
|
||||
{
|
||||
name: 'FCS',
|
||||
value: '校验序列',
|
||||
size: '4 字节',
|
||||
width: '8%'
|
||||
}
|
||||
]
|
||||
|
||||
const transferSteps = [
|
||||
{
|
||||
title: '封装成帧',
|
||||
desc: '发送方将数据封装成以太网帧,添加源 MAC 和目的 MAC 地址'
|
||||
},
|
||||
{
|
||||
title: '发送到交换机',
|
||||
desc: '帧通过物理介质(网线或 WiFi)发送到交换机'
|
||||
},
|
||||
{
|
||||
title: '交换机转发',
|
||||
desc: '交换机根据目的 MAC 地址,将帧转发到对应端口'
|
||||
},
|
||||
{
|
||||
title: '接收方处理',
|
||||
desc: '接收方检查目的 MAC 地址,匹配后接收并处理数据'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.data-link-demo {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.demo-header .icon { font-size: 1.5rem; }
|
||||
.demo-header .title { font-weight: 700; font-size: 1.1rem; }
|
||||
.demo-header .subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
|
||||
|
||||
.lan-scene {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.lan-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.lan-devices {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
gap: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.lan-device {
|
||||
text-align: center;
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.lan-device:hover {
|
||||
border-color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.lan-device.active {
|
||||
border-color: var(--vp-c-brand);
|
||||
background: var(--vp-c-brand-soft);
|
||||
}
|
||||
|
||||
.lan-device.sender {
|
||||
border-color: #10b981;
|
||||
background: rgba(16, 185, 129, 0.1);
|
||||
}
|
||||
|
||||
.lan-device.receiver {
|
||||
border-color: #3b82f6;
|
||||
background: rgba(59, 130, 246, 0.1);
|
||||
}
|
||||
|
||||
.device-icon {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.device-name {
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.device-mac {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 0.7rem;
|
||||
color: var(--vp-c-text-2);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.device-role {
|
||||
font-size: 0.75rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
border-radius: 10px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.switch {
|
||||
text-align: center;
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border: 2px dashed var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.switch-icon {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.switch-name {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.switch-desc {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.frame-structure {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.frame-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.frame-visual {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.frame-fields {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.frame-field {
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
padding: 0.75rem 0.5rem;
|
||||
text-align: center;
|
||||
background: var(--vp-c-bg-soft);
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.frame-field.highlighted {
|
||||
border-color: var(--vp-c-brand);
|
||||
background: var(--vp-c-brand-soft);
|
||||
}
|
||||
|
||||
.field-name {
|
||||
font-weight: 600;
|
||||
font-size: 0.8rem;
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.field-value {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 0.75rem;
|
||||
color: var(--vp-c-text-2);
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.field-size {
|
||||
font-size: 0.7rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.transfer-process {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.process-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.process-steps {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.process-step {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
padding: 0.75rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.step-number {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.step-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.step-title {
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.step-desc {
|
||||
font-size: 0.85rem;
|
||||
color: var(--vp-c-text-2);
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.arp-section {
|
||||
border-top: 1px solid var(--vp-c-divider);
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
|
||||
.arp-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.arp-example {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.arp-question,
|
||||
.arp-answer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 0.75rem;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.arp-question {
|
||||
background: rgba(59, 130, 246, 0.1);
|
||||
}
|
||||
|
||||
.arp-answer {
|
||||
background: rgba(16, 185, 129, 0.1);
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.question-icon,
|
||||
.answer-icon {
|
||||
font-size: 1.5rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.question-text,
|
||||
.answer-text {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.arp-arrow {
|
||||
text-align: center;
|
||||
font-size: 0.85rem;
|
||||
color: var(--vp-c-text-2);
|
||||
margin: 0.75rem 0;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.frame-fields {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.frame-field {
|
||||
min-width: 100px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
+530
@@ -0,0 +1,530 @@
|
||||
<template>
|
||||
<div class="ds-overview-demo">
|
||||
<div class="demo-header">
|
||||
<span class="icon">🏗️</span>
|
||||
<span class="title">数据结构全景图</span>
|
||||
<span class="subtitle">不同场景选择不同的数据组织方式</span>
|
||||
</div>
|
||||
|
||||
<div class="structure-map">
|
||||
<div class="map-intro">
|
||||
数据结构就像整理房间的方式:把衣服放进衣柜、书放在书架、杂物放抽屉
|
||||
</div>
|
||||
|
||||
<div class="structure-categories">
|
||||
<div
|
||||
v-for="category in categories"
|
||||
:key="category.id"
|
||||
:class="['category-card', { active: activeCategory === category.id }]"
|
||||
@click="activeCategory = category.id"
|
||||
>
|
||||
<div class="category-icon">{{ category.icon }}</div>
|
||||
<div class="category-name">{{ category.name }}</div>
|
||||
<div class="category-desc">{{ category.desc }}</div>
|
||||
<div class="category-examples">
|
||||
<span
|
||||
v-for="example in category.examples"
|
||||
:key="example"
|
||||
class="example-tag"
|
||||
>
|
||||
{{ example }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 详细说明 -->
|
||||
<div class="category-detail">
|
||||
<div class="detail-header">
|
||||
<span class="detail-icon">{{ currentCategory.icon }}</span>
|
||||
<span class="detail-title">{{ currentCategory.name }}</span>
|
||||
</div>
|
||||
|
||||
<div class="detail-content">
|
||||
<div class="detail-section">
|
||||
<div class="section-title">特点</div>
|
||||
<div class="feature-grid">
|
||||
<div
|
||||
v-for="(feature, index) in currentCategory.features"
|
||||
:key="index"
|
||||
class="feature-item"
|
||||
>
|
||||
<span class="feature-icon">✓</span>
|
||||
<span class="feature-text">{{ feature }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-section">
|
||||
<div class="section-title">适用场景</div>
|
||||
<div class="scenario-list">
|
||||
<div
|
||||
v-for="(scenario, index) in currentCategory.scenarios"
|
||||
:key="index"
|
||||
class="scenario-card"
|
||||
>
|
||||
<div class="scenario-icon">{{ scenario.icon }}</div>
|
||||
<div class="scenario-content">
|
||||
<div class="scenario-title">{{ scenario.title }}</div>
|
||||
<div class="scenario-desc">{{ scenario.desc }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-section">
|
||||
<div class="section-title">操作复杂度</div>
|
||||
<div class="complexity-table">
|
||||
<div class="table-header">
|
||||
<span class="header-cell">操作</span>
|
||||
<span class="header-cell">平均时间</span>
|
||||
</div>
|
||||
<div
|
||||
v-for="(op, index) in currentCategory.complexity"
|
||||
:key="index"
|
||||
class="table-row"
|
||||
>
|
||||
<span class="data-cell">{{ op.operation }}</span>
|
||||
<span class="data-cell highlight">{{ op.time }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 类比说明 -->
|
||||
<div class="analogy-section">
|
||||
<div class="analogy-title">生活类比</div>
|
||||
<div class="analogy-content">
|
||||
<div class="analogy-text">{{ currentCategory.analogy.text }}</div>
|
||||
<div class="analogy-example">{{ currentCategory.analogy.example }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const activeCategory = ref('linear')
|
||||
|
||||
const categories = [
|
||||
{
|
||||
id: 'linear',
|
||||
name: '线性结构',
|
||||
icon: '📚',
|
||||
desc: '数据按顺序排列,像一排书',
|
||||
examples: ['数组', '链表', '栈', '队列'],
|
||||
features: [
|
||||
'数据元素之间一对一关系',
|
||||
'有明确的先后顺序',
|
||||
'可以是连续存储或链式存储'
|
||||
],
|
||||
scenarios: [
|
||||
{
|
||||
icon: '📝',
|
||||
title: '数组:列表数据',
|
||||
desc: '存储学生成绩、商品价格等有序数据'
|
||||
},
|
||||
{
|
||||
icon: '🔄',
|
||||
title: '栈:撤销操作',
|
||||
desc: '文本编辑器的撤销功能,后进先出'
|
||||
},
|
||||
{
|
||||
icon: '🎫',
|
||||
title: '队列:任务调度',
|
||||
desc: '打印队列、任务队列,先进先出'
|
||||
}
|
||||
],
|
||||
complexity: [
|
||||
{ operation: '访问元素', time: 'O(1)' },
|
||||
{ operation: '插入/删除', time: 'O(n)' }
|
||||
],
|
||||
analogy: {
|
||||
text: '像一列火车,车厢按顺序连接',
|
||||
example: '要找到第 5 节车厢,直接数过去;要插入新车厢,需要断开连接'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'hash',
|
||||
name: '哈希结构',
|
||||
icon: '🗂️',
|
||||
desc: '通过关键词快速查找',
|
||||
examples: ['哈希表', '字典', '集合'],
|
||||
features: [
|
||||
'通过键值对存储数据',
|
||||
'查找速度极快',
|
||||
'数据之间没有顺序关系'
|
||||
],
|
||||
scenarios: [
|
||||
{
|
||||
icon: '📖',
|
||||
title: '字典:单词查找',
|
||||
desc: '根据英文单词快速找到中文释义'
|
||||
},
|
||||
{
|
||||
icon: '👤',
|
||||
title: '用户信息:ID 查询',
|
||||
desc: '根据用户 ID 快速获取用户资料'
|
||||
},
|
||||
{
|
||||
icon: '🛒',
|
||||
title: '购物车:商品管理',
|
||||
desc: '记录商品 ID 和数量,快速结算'
|
||||
}
|
||||
],
|
||||
complexity: [
|
||||
{ operation: '查找', time: 'O(1)' },
|
||||
{ operation: '插入/删除', time: 'O(1)' }
|
||||
],
|
||||
analogy: {
|
||||
text: '像图书馆的索引卡片',
|
||||
example: '不用在一排排书架上找,直接查索引就能找到位置'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'tree',
|
||||
name: '树形结构',
|
||||
icon: '🌳',
|
||||
desc: '层级关系,像家谱',
|
||||
examples: ['二叉树', 'B 树', '堆'],
|
||||
features: [
|
||||
'一对多的层级关系',
|
||||
'有明确的根节点',
|
||||
'适合表示分类和层级'
|
||||
],
|
||||
scenarios: [
|
||||
{
|
||||
icon: '📁',
|
||||
title: '文件系统:目录树',
|
||||
desc: '文件夹和文件的层级组织'
|
||||
},
|
||||
{
|
||||
icon: '🏢',
|
||||
title: '组织架构:管理树',
|
||||
desc: '公司管理层级关系'
|
||||
},
|
||||
{
|
||||
icon: '💻',
|
||||
title: 'HTML:DOM 树',
|
||||
desc: '网页元素的嵌套结构'
|
||||
}
|
||||
],
|
||||
complexity: [
|
||||
{ operation: '查找', time: 'O(log n)' },
|
||||
{ operation: '插入/删除', time: 'O(log n)' }
|
||||
],
|
||||
analogy: {
|
||||
text: '像家谱树或公司组织架构',
|
||||
example: '从根节点(祖先)开始,一层层向下找,路径唯一'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'graph',
|
||||
name: '图结构',
|
||||
icon: '🕸️',
|
||||
desc: '复杂关系网络',
|
||||
examples: ['有向图', '无向图', '网络图'],
|
||||
features: [
|
||||
'多对多的复杂关系',
|
||||
'节点之间可以任意连接',
|
||||
'可以表示复杂网络'
|
||||
],
|
||||
scenarios: [
|
||||
{
|
||||
icon: '🗺️',
|
||||
title: '地图:路径规划',
|
||||
desc: '城市之间的道路连接,导航系统'
|
||||
},
|
||||
{
|
||||
icon: '👥',
|
||||
title: '社交网络:好友关系',
|
||||
desc: '用户之间的关注、好友关系'
|
||||
},
|
||||
{
|
||||
icon: '🔗',
|
||||
title: '网页:链接关系',
|
||||
desc: '网页之间的超链接网络'
|
||||
}
|
||||
],
|
||||
complexity: [
|
||||
{ operation: '遍历', time: 'O(V + E)' },
|
||||
{ operation: '最短路径', time: 'O(E + V log V)' }
|
||||
],
|
||||
analogy: {
|
||||
text: '像地铁线路图或航空网络',
|
||||
example: '多个站点,多条线路,站点之间可以有多种连接方式'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
const currentCategory = computed(() => categories.find(c => c.id === activeCategory.value))
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.ds-overview-demo {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.demo-header .icon { font-size: 1.5rem; }
|
||||
.demo-header .title { font-weight: 700; font-size: 1.1rem; }
|
||||
.demo-header .subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
|
||||
|
||||
.structure-map {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.map-intro {
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg);
|
||||
border-left: 4px solid var(--vp-c-brand);
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.structure-categories {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.category-card {
|
||||
padding: 1.25rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.category-card:hover {
|
||||
border-color: var(--vp-c-brand);
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
|
||||
.category-card.active {
|
||||
border-color: var(--vp-c-brand);
|
||||
background: var(--vp-c-brand-soft);
|
||||
}
|
||||
|
||||
.category-icon {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.category-name {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.category-desc {
|
||||
font-size: 0.85rem;
|
||||
color: var(--vp-c-text-2);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.category-examples {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.example-tag {
|
||||
padding: 0.25rem 0.6rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 12px;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.category-detail {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.detail-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 1.5rem;
|
||||
padding-bottom: 1rem;
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.detail-icon {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.detail-title {
|
||||
font-weight: 600;
|
||||
font-size: 1.1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.detail-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.detail-section {}
|
||||
|
||||
.section-title {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.feature-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.feature-item {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
align-items: start;
|
||||
padding: 0.75rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.feature-icon {
|
||||
color: #10b981;
|
||||
font-weight: 700;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.feature-text {
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.scenario-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.scenario-card {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.scenario-icon {
|
||||
font-size: 1.5rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.scenario-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.scenario-title {
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.scenario-desc {
|
||||
font-size: 0.85rem;
|
||||
color: var(--vp-c-text-2);
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.complexity-table {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.table-header {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.header-cell {
|
||||
padding: 0.6rem;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.table-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
border-top: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.data-cell {
|
||||
padding: 0.6rem;
|
||||
font-size: 0.85rem;
|
||||
text-align: center;
|
||||
font-family: 'Courier New', monospace;
|
||||
}
|
||||
|
||||
.data-cell.highlight {
|
||||
color: var(--vp-c-brand);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.analogy-section {
|
||||
border-top: 1px solid var(--vp-c-divider);
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
|
||||
.analogy-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.analogy-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.analogy-text {
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 6px;
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.analogy-example {
|
||||
padding: 0.75rem;
|
||||
background: var(--vp-c-bg);
|
||||
border-left: 3px solid var(--vp-c-brand);
|
||||
font-size: 0.9rem;
|
||||
color: var(--vp-c-text-2);
|
||||
line-height: 1.5;
|
||||
}
|
||||
</style>
|
||||
+486
@@ -0,0 +1,486 @@
|
||||
<template>
|
||||
<div class="ds-selector-demo">
|
||||
<div class="demo-header">
|
||||
<span class="icon">🎯</span>
|
||||
<span class="title">如何选择合适的数据结构?</span>
|
||||
<span class="subtitle">根据场景需求做出最佳选择</span>
|
||||
</div>
|
||||
|
||||
<div class="scenario-selector">
|
||||
<div class="selector-title">你的使用场景是?</div>
|
||||
<div class="scenario-grid">
|
||||
<div
|
||||
v-for="scenario in scenarios"
|
||||
:key="scenario.id"
|
||||
:class="['scenario-card', { active: activeScenario === scenario.id }]"
|
||||
@click="activeScenario = scenario.id"
|
||||
>
|
||||
<div class="scenario-icon">{{ scenario.icon }}</div>
|
||||
<div class="scenario-name">{{ scenario.name }}</div>
|
||||
<div class="scenario-desc">{{ scenario.desc }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 推荐结果 -->
|
||||
<div v-if="activeScenario" class="recommendation">
|
||||
<div class="rec-header">
|
||||
<span class="rec-icon">💡</span>
|
||||
<span class="rec-title">推荐使用:{{ currentScenario.recommendation }}</span>
|
||||
</div>
|
||||
|
||||
<div class="rec-reason">
|
||||
<div class="reason-title">为什么?</div>
|
||||
<div class="reason-list">
|
||||
<div
|
||||
v-for="(reason, index) in currentScenario.reasons"
|
||||
:key="index"
|
||||
class="reason-item"
|
||||
>
|
||||
<span class="reason-bullet">✓</span>
|
||||
<span class="reason-text">{{ reason }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rec-example">
|
||||
<div class="example-title">实际例子</div>
|
||||
<div class="example-content">{{ currentScenario.example }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 快速参考表 -->
|
||||
<div class="quick-reference">
|
||||
<div class="ref-title">快速参考表</div>
|
||||
<table class="ref-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>场景需求</th>
|
||||
<th>推荐数据结构</th>
|
||||
<th>时间复杂度</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(row, index) in referenceTable" :key="index">
|
||||
<td>{{ row.scenario }}</td>
|
||||
<td>{{ row.structure }}</td>
|
||||
<td class="complexity">{{ row.complexity }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- 决策流程 -->
|
||||
<div class="decision-flow">
|
||||
<div class="flow-title">选择决策流程</div>
|
||||
<div class="flow-diagram">
|
||||
<div class="flow-step question">
|
||||
<div class="step-icon">❓</div>
|
||||
<div class="step-text">需要快速访问元素?</div>
|
||||
</div>
|
||||
<div class="flow-branch">
|
||||
<div class="branch-yes">
|
||||
<div class="branch-label">是</div>
|
||||
<div class="flow-result">数组 / 哈希表</div>
|
||||
</div>
|
||||
<div class="branch-no">
|
||||
<div class="branch-label">否</div>
|
||||
<div class="flow-step question">
|
||||
<div class="step-text">需要频繁插入删除?</div>
|
||||
</div>
|
||||
<div class="flow-branch">
|
||||
<div class="branch-yes">
|
||||
<div class="branch-label">是</div>
|
||||
<div class="flow-result">链表</div>
|
||||
</div>
|
||||
<div class="branch-no">
|
||||
<div class="branch-label">否</div>
|
||||
<div class="flow-step question">
|
||||
<div class="step-text">需要保持顺序?</div>
|
||||
</div>
|
||||
<div class="flow-branch">
|
||||
<div class="branch-yes">
|
||||
<div class="branch-label">是</div>
|
||||
<div class="flow-result">栈 / 队列</div>
|
||||
</div>
|
||||
<div class="branch-no">
|
||||
<div class="branch-label">否</div>
|
||||
<div class="flow-result">树 / 图</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const activeScenario = ref(null)
|
||||
|
||||
const scenarios = [
|
||||
{
|
||||
id: 'lookup',
|
||||
icon: '🔍',
|
||||
name: '快速查找',
|
||||
desc: '根据关键词快速找到对应数据',
|
||||
recommendation: '哈希表',
|
||||
reasons: [
|
||||
'平均查找时间 O(1),瞬间找到',
|
||||
'键值对存储,语义清晰',
|
||||
'无需遍历整个数据集'
|
||||
],
|
||||
example: '用户 ID 查找用户资料、字典查词、缓存系统'
|
||||
},
|
||||
{
|
||||
id: 'ordered',
|
||||
icon: '📊',
|
||||
name: '保持顺序',
|
||||
desc: '数据需要按插入顺序或特定顺序存储',
|
||||
recommendation: '数组 或 链表',
|
||||
reasons: [
|
||||
'数组支持索引直接访问',
|
||||
'链表可以灵活调整大小',
|
||||
'按位置访问速度快'
|
||||
],
|
||||
example: '学生成绩列表、时间序列数据、排行榜'
|
||||
},
|
||||
{
|
||||
id: 'lifo',
|
||||
icon: '🥞',
|
||||
name: '后进先出',
|
||||
desc: '最后进入的最先处理',
|
||||
recommendation: '栈',
|
||||
reasons: [
|
||||
'只能在栈顶操作',
|
||||
'入栈出栈都是 O(1)',
|
||||
'适合回溯和撤销操作'
|
||||
],
|
||||
example: '浏览器后退、编辑器撤销、函数调用栈'
|
||||
},
|
||||
{
|
||||
id: 'fifo',
|
||||
icon: '🚶',
|
||||
name: '先进先出',
|
||||
desc: '先来的先处理',
|
||||
recommendation: '队列',
|
||||
reasons: [
|
||||
'一端入队,另一端出队',
|
||||
'入队出队都是 O(1)',
|
||||
'公平的调度方式'
|
||||
],
|
||||
example: '打印队列、任务调度、消息队列'
|
||||
},
|
||||
{
|
||||
id: 'hierarchy',
|
||||
icon: '🌳',
|
||||
name: '层级关系',
|
||||
desc: '数据之间有父子层级关系',
|
||||
recommendation: '树',
|
||||
reasons: [
|
||||
'清晰表达层级结构',
|
||||
'查找效率 O(log n)',
|
||||
'支持多种遍历方式'
|
||||
],
|
||||
example: '文件系统、组织架构、HTML DOM'
|
||||
},
|
||||
{
|
||||
id: 'relationship',
|
||||
icon: '🕸️',
|
||||
name: '复杂关系',
|
||||
desc: '数据之间有多对多的复杂连接',
|
||||
recommendation: '图',
|
||||
reasons: [
|
||||
'可以表示任意关系',
|
||||
'支持路径搜索算法',
|
||||
'适合网络和社交关系'
|
||||
],
|
||||
example: '社交网络、地图导航、网页链接'
|
||||
}
|
||||
]
|
||||
|
||||
const referenceTable = [
|
||||
{ scenario: '随机访问', structure: '数组', complexity: 'O(1)' },
|
||||
{ scenario: '快速查找', structure: '哈希表', complexity: 'O(1)' },
|
||||
{ scenario: '有序查找', structure: '二叉搜索树', complexity: 'O(log n)' },
|
||||
{ scenario: '频繁插入删除', structure: '链表', complexity: 'O(1)' },
|
||||
{ scenario: '撤销操作', structure: '栈', complexity: 'O(1)' },
|
||||
{ scenario: '任务调度', structure: '队列', complexity: 'O(1)' }
|
||||
]
|
||||
|
||||
const currentScenario = computed(() => {
|
||||
return scenarios.find(s => s.id === activeScenario.value)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.ds-selector-demo {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.demo-header .icon { font-size: 1.5rem; }
|
||||
.demo-header .title { font-weight: 700; font-size: 1.1rem; }
|
||||
.demo-header .subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
|
||||
|
||||
.scenario-selector {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.selector-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.scenario-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.scenario-card {
|
||||
padding: 1.25rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.scenario-card:hover {
|
||||
border-color: var(--vp-c-brand);
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
|
||||
.scenario-card.active {
|
||||
border-color: var(--vp-c-brand);
|
||||
background: var(--vp-c-brand-soft);
|
||||
}
|
||||
|
||||
.scenario-icon {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.scenario-name {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.scenario-desc {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.recommendation {
|
||||
background: var(--vp-c-bg);
|
||||
border: 2px solid var(--vp-c-brand);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
animation: slideIn 0.3s ease-out;
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
from { opacity: 0; transform: translateY(-10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
.rec-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.rec-icon {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.rec-title {
|
||||
font-weight: 600;
|
||||
font-size: 1.1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.rec-reason {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.reason-title {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.reason-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.reason-item {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.reason-bullet {
|
||||
color: #10b981;
|
||||
font-weight: 700;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.reason-text {
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.rec-example {
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.example-title {
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0.5rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.example-content {
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.6;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.quick-reference {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.ref-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.ref-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.ref-table th {
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
padding: 0.75rem;
|
||||
text-align: left;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.ref-table td {
|
||||
padding: 0.75rem;
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.complexity {
|
||||
font-family: 'Courier New', monospace;
|
||||
color: var(--vp-c-brand);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.decision-flow {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.flow-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.flow-diagram {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.flow-step {
|
||||
padding: 0.75rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.flow-step.question {
|
||||
background: rgba(59, 130, 246, 0.1);
|
||||
border-color: #3b82f6;
|
||||
}
|
||||
|
||||
.step-icon {
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.step-text {
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.flow-branch {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
.branch-yes,
|
||||
.branch-no {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.branch-label {
|
||||
text-align: center;
|
||||
padding: 0.5rem;
|
||||
background: var(--vp-c-brand-soft);
|
||||
border-radius: 4px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.flow-result {
|
||||
text-align: center;
|
||||
padding: 0.75rem;
|
||||
background: #10b981;
|
||||
color: white;
|
||||
border-radius: 6px;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
+637
@@ -0,0 +1,637 @@
|
||||
<template>
|
||||
<div class="est-demo">
|
||||
<div class="demo-header">
|
||||
<span class="icon">🔗</span>
|
||||
<span class="title">编码、存储与传输的协作</span>
|
||||
<span class="subtitle">三大系统如何协同处理数据</span>
|
||||
</div>
|
||||
|
||||
<div class="scenario-selector">
|
||||
<div class="selector-label">选择场景:</div>
|
||||
<div class="scenario-buttons">
|
||||
<button
|
||||
v-for="scenario in scenarios"
|
||||
:key="scenario.id"
|
||||
:class="['scenario-btn', { active: activeScenario === scenario.id }]"
|
||||
@click="activeScenario = scenario.id"
|
||||
>
|
||||
{{ scenario.icon }} {{ scenario.name }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="collab-diagram">
|
||||
<div class="diagram-flow">
|
||||
<!-- 编码阶段 -->
|
||||
<div class="flow-stage encoding-stage">
|
||||
<div class="stage-header">
|
||||
<span class="stage-icon">🔤</span>
|
||||
<span class="stage-title">编码</span>
|
||||
</div>
|
||||
<div class="stage-content">
|
||||
<div class="input-box">
|
||||
<div class="box-label">原始数据</div>
|
||||
<div class="box-value">{{ currentScenario.encoding.input }}</div>
|
||||
</div>
|
||||
<div class="arrow">↓</div>
|
||||
<div class="output-box">
|
||||
<div class="box-label">编码后</div>
|
||||
<div class="box-value code">{{ currentScenario.encoding.output }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 存储阶段 -->
|
||||
<div class="flow-stage storage-stage">
|
||||
<div class="stage-header">
|
||||
<span class="stage-icon">💾</span>
|
||||
<span class="stage-title">存储</span>
|
||||
</div>
|
||||
<div class="stage-content">
|
||||
<div class="storage-visual">
|
||||
<div class="storage-blocks">
|
||||
<div
|
||||
v-for="(block, index) in currentScenario.storage.blocks"
|
||||
:key="index"
|
||||
class="storage-block"
|
||||
:title="block"
|
||||
>
|
||||
{{ block }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="storage-info">
|
||||
<div class="info-item">
|
||||
<span class="info-label">位置:</span>
|
||||
<span class="info-value">{{ currentScenario.storage.location }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">大小:</span>
|
||||
<span class="info-value">{{ currentScenario.storage.size }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 传输阶段 -->
|
||||
<div class="flow-stage transmission-stage">
|
||||
<div class="stage-header">
|
||||
<span class="stage-icon">📡</span>
|
||||
<span class="stage-title">传输</span>
|
||||
</div>
|
||||
<div class="stage-content">
|
||||
<div class="transmission-flow">
|
||||
<div class="transmission-packet">
|
||||
<div class="packet-header">数据包</div>
|
||||
<div class="packet-body">
|
||||
<div class="packet-layer" v-for="(layer, index) in currentScenario.transmission.layers" :key="index">
|
||||
<span class="layer-name">{{ layer.name }}:</span>
|
||||
<span class="layer-value">{{ layer.value }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="transmission-info">
|
||||
<div class="info-item">
|
||||
<span class="info-label">协议:</span>
|
||||
<span class="info-value">{{ currentScenario.transmission.protocol }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">路径:</span>
|
||||
<span class="info-value">{{ currentScenario.transmission.path }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 协作关系 -->
|
||||
<div class="collab-relationships">
|
||||
<div class="relationship-arrow encoding-to-storage">
|
||||
<span class="arrow-text">{{ currentScenario.relationships.encodingToStorage }}</span>
|
||||
<span class="arrow-icon">→</span>
|
||||
</div>
|
||||
<div class="relationship-arrow storage-to-transmission">
|
||||
<span class="arrow-text">{{ currentScenario.relationships.storageToTransmission }}</span>
|
||||
<span class="arrow-icon">→</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 关键要点 -->
|
||||
<div class="key-points">
|
||||
<div class="points-title">协作要点</div>
|
||||
<div class="points-grid">
|
||||
<div v-for="(point, index) in currentScenario.points" :key="index" class="point-card">
|
||||
<div class="point-icon">{{ point.icon }}</div>
|
||||
<div class="point-content">
|
||||
<div class="point-title">{{ point.title }}</div>
|
||||
<div class="point-desc">{{ point.desc }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const activeScenario = ref('text-file')
|
||||
|
||||
const scenarios = [
|
||||
{
|
||||
id: 'text-file',
|
||||
name: '保存文本文件',
|
||||
icon: '📝'
|
||||
},
|
||||
{
|
||||
id: 'upload-image',
|
||||
name: '上传图片',
|
||||
icon: '🖼️'
|
||||
},
|
||||
{
|
||||
id: 'stream-video',
|
||||
name: '流媒体播放',
|
||||
icon: '🎬'
|
||||
},
|
||||
{
|
||||
id: 'send-message',
|
||||
name: '发送消息',
|
||||
icon: '💬'
|
||||
}
|
||||
]
|
||||
|
||||
const scenarioData = {
|
||||
'text-file': {
|
||||
encoding: {
|
||||
input: '你好',
|
||||
output: 'U+4F60 U+597D'
|
||||
},
|
||||
storage: {
|
||||
location: '文档文件夹 /hello.txt',
|
||||
size: '6 字节 (UTF-8)',
|
||||
blocks: ['E4', 'BD', 'A0', 'E5', 'A5', 'BD']
|
||||
},
|
||||
transmission: {
|
||||
protocol: 'HTTP + TCP/IP',
|
||||
path: '客户端 → 服务器 → 云存储',
|
||||
layers: [
|
||||
{ name: '应用层', value: 'HTTP POST' },
|
||||
{ name: '传输层', value: 'TCP 端口 443' },
|
||||
{ name: '网络层', value: 'IP 数据包' }
|
||||
]
|
||||
},
|
||||
relationships: {
|
||||
encodingToStorage: 'UTF-8 编码后的字节序列写入磁盘',
|
||||
storageToTransmission: '读取文件并通过网络发送'
|
||||
},
|
||||
points: [
|
||||
{
|
||||
icon: '🔤',
|
||||
title: '编码统一',
|
||||
desc: '使用 UTF-8 编码确保中文字符正确存储和传输'
|
||||
},
|
||||
{
|
||||
icon: '📦',
|
||||
title: '文件封装',
|
||||
desc: '文本内容被封装成 .txt 文件格式存储'
|
||||
},
|
||||
{
|
||||
icon: '🔄',
|
||||
title: '协议转换',
|
||||
desc: '存储时用文件系统协议,传输时用 HTTP 协议'
|
||||
}
|
||||
]
|
||||
},
|
||||
'upload-image': {
|
||||
encoding: {
|
||||
input: '图片数据',
|
||||
output: 'JPEG 压缩编码'
|
||||
},
|
||||
storage: {
|
||||
location: '相册 /photo.jpg',
|
||||
size: '2.5 MB',
|
||||
blocks: ['FF', 'D8', 'FF', 'E0', '...', 'FF', 'D9']
|
||||
},
|
||||
transmission: {
|
||||
protocol: 'HTTPS + MIME multipart',
|
||||
path: '手机 → API 网关 → 对象存储',
|
||||
layers: [
|
||||
{ name: '应用层', value: 'HTTPS POST' },
|
||||
{ name: '传输层', value: 'TLS 加密' },
|
||||
{ name: '网络层', value: 'IP 分片' }
|
||||
]
|
||||
},
|
||||
relationships: {
|
||||
encodingToStorage: 'JPEG 压缩编码减少文件大小',
|
||||
storageToTransmission: '二进制数据分块上传'
|
||||
},
|
||||
points: [
|
||||
{
|
||||
icon: '🗜️',
|
||||
title: '压缩编码',
|
||||
desc: 'JPEG 压缩算法减少图片体积,节省存储空间'
|
||||
},
|
||||
{
|
||||
icon: '🔐',
|
||||
title: '安全传输',
|
||||
desc: 'HTTPS 加密保护图片数据在网络传输中的安全'
|
||||
},
|
||||
{
|
||||
icon: '⚡',
|
||||
title: '分块上传',
|
||||
desc: '大文件分块传输,支持断点续传'
|
||||
}
|
||||
]
|
||||
},
|
||||
'stream-video': {
|
||||
encoding: {
|
||||
input: '视频流',
|
||||
output: 'H.264 编码'
|
||||
},
|
||||
storage: {
|
||||
location: 'CDN 缓存节点',
|
||||
size: '动态调整',
|
||||
blocks: ['帧1', '帧2', '帧3', '...']
|
||||
},
|
||||
transmission: {
|
||||
protocol: 'HLS + DASH',
|
||||
path: '服务器 → CDN → 用户设备',
|
||||
layers: [
|
||||
{ name: '应用层', value: 'HLS 播放列表' },
|
||||
{ name: '传输层', value: 'TCP 流式' },
|
||||
{ name: '网络层', value: 'UDP 可能' }
|
||||
]
|
||||
},
|
||||
relationships: {
|
||||
encodingToStorage: '视频分段存储在 CDN',
|
||||
storageToTransmission: '根据网络状况自适应码率'
|
||||
},
|
||||
points: [
|
||||
{
|
||||
icon: '🎬',
|
||||
title: '流式编码',
|
||||
desc: 'H.264 视频编码压缩,适合网络传输'
|
||||
},
|
||||
{
|
||||
icon: '🌐',
|
||||
title: 'CDN 加速',
|
||||
desc: '内容分发网络缓存视频,就近提供服务'
|
||||
},
|
||||
{
|
||||
icon: '📊',
|
||||
title: '自适应码率',
|
||||
desc: '根据网络状况动态调整视频质量'
|
||||
}
|
||||
]
|
||||
},
|
||||
'send-message': {
|
||||
encoding: {
|
||||
input: '消息内容',
|
||||
output: 'JSON 格式'
|
||||
},
|
||||
storage: {
|
||||
location: '本地数据库 + 服务器',
|
||||
size: '约 200 字节',
|
||||
blocks: ['JSON格式']
|
||||
},
|
||||
transmission: {
|
||||
protocol: 'WebSocket',
|
||||
path: '发送方 → 即时通讯服务器 → 接收方',
|
||||
layers: [
|
||||
{ name: '应用层', value: 'WebSocket 帧' },
|
||||
{ name: '传输层', value: 'TCP 长连接' },
|
||||
{ name: '网络层', value: 'IP 路由' }
|
||||
]
|
||||
},
|
||||
relationships: {
|
||||
encodingToStorage: 'JSON 格式便于解析和存储',
|
||||
storageToTransmission: 'WebSocket 保持实时连接'
|
||||
},
|
||||
points: [
|
||||
{
|
||||
icon: '📨',
|
||||
title: '实时推送',
|
||||
desc: 'WebSocket 长连接实现消息即时送达'
|
||||
},
|
||||
{
|
||||
icon: '💾',
|
||||
title: '双重存储',
|
||||
desc: '本地存储离线消息,服务器存储历史记录'
|
||||
},
|
||||
{
|
||||
icon: '🔗',
|
||||
title: 'JSON 编码',
|
||||
desc: '结构化数据格式,易于解析和扩展'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
const currentScenario = computed(() => scenarioData[activeScenario.value])
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.est-demo {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.demo-header .icon { font-size: 1.5rem; }
|
||||
.demo-header .title { font-weight: 700; font-size: 1.1rem; }
|
||||
.demo-header .subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
|
||||
|
||||
.scenario-selector {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.selector-label {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.scenario-buttons {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.scenario-btn {
|
||||
padding: 0.6rem 1rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 0.9rem;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.scenario-btn:hover {
|
||||
border-color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.scenario-btn.active {
|
||||
background: var(--vp-c-brand);
|
||||
border-color: var(--vp-c-brand);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.collab-diagram {
|
||||
position: relative;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.diagram-flow {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
@media (max-width: 968px) {
|
||||
.diagram-flow {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.flow-stage {
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg);
|
||||
}
|
||||
|
||||
.stage-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
padding-bottom: 0.75rem;
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.stage-icon { font-size: 1.3rem; }
|
||||
.stage-title { font-weight: 600; font-size: 0.95rem; }
|
||||
|
||||
.stage-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.input-box,
|
||||
.output-box {
|
||||
padding: 0.75rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.box-label {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.box-value {
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.box-value.code {
|
||||
font-family: 'Courier New', monospace;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.arrow {
|
||||
text-align: center;
|
||||
font-size: 1.2rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.storage-visual {
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 6px;
|
||||
padding: 0.75rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.storage-blocks {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
|
||||
.storage-block {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--vp-c-brand-soft);
|
||||
border: 1px solid var(--vp-c-brand);
|
||||
border-radius: 4px;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 0.75rem;
|
||||
color: var(--vp-c-brand);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.storage-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
color: var(--vp-c-text-2);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
color: var(--vp-c-text-1);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.transmission-flow {
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 6px;
|
||||
padding: 0.75rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.transmission-packet {
|
||||
border: 2px solid var(--vp-c-brand);
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.packet-header {
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
padding: 0.5rem;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.packet-body {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.packet-layer {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.8rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.packet-layer:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.layer-name {
|
||||
color: var(--vp-c-text-2);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.layer-value {
|
||||
color: var(--vp-c-text-1);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.collab-relationships {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
gap: 1rem;
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.relationship-arrow {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.arrow-text {
|
||||
font-size: 0.85rem;
|
||||
color: var(--vp-c-text-1);
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.arrow-icon {
|
||||
font-size: 1.5rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.key-points {
|
||||
border-top: 1px solid var(--vp-c-divider);
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
|
||||
.points-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.points-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.point-card {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
padding: 0.75rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.point-icon {
|
||||
font-size: 1.5rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.point-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.point-title {
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.point-desc {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
line-height: 1.4;
|
||||
}
|
||||
</style>
|
||||
+309
@@ -0,0 +1,309 @@
|
||||
<template>
|
||||
<div class="graph-structure-demo">
|
||||
<div class="demo-header">
|
||||
<span class="icon">🕸️</span>
|
||||
<span class="title">图结构:复杂关系的表示</span>
|
||||
<span class="subtitle">节点和边的网络</span>
|
||||
</div>
|
||||
|
||||
<div class="graph-types">
|
||||
<div class="type-selector">
|
||||
<button
|
||||
:class="['type-btn', { active: graphType === 'undirected' }]"
|
||||
@click="graphType = 'undirected'"
|
||||
>
|
||||
无向图
|
||||
</button>
|
||||
<button
|
||||
:class="['type-btn', { active: graphType === 'directed' }]"
|
||||
@click="graphType = 'directed'"
|
||||
>
|
||||
有向图
|
||||
</button>
|
||||
<button
|
||||
:class="['type-btn', { active: graphType === 'weighted' }]"
|
||||
@click="graphType = 'weighted'"
|
||||
>
|
||||
带权图
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="graph-visualization">
|
||||
<svg viewBox="0 0 400 300" class="graph-svg">
|
||||
<!-- 连接线 -->
|
||||
<line
|
||||
v-for="edge in edges"
|
||||
:key="edge.id"
|
||||
:x1="nodes[edge.from].x"
|
||||
:y1="nodes[edge.from].y"
|
||||
:x2="nodes[edge.to].x"
|
||||
:y2="nodes[edge.to].y"
|
||||
:stroke="edge.weight ? '#3b82f6' : 'var(--vp-c-divider)'"
|
||||
:stroke-width="edge.weight ? '3' : '2'"
|
||||
:marker-end="graphType === 'directed' ? 'url(#arrow)' : ''"
|
||||
/>
|
||||
|
||||
<!-- 箭头定义 -->
|
||||
<defs v-if="graphType === 'directed'">
|
||||
<marker
|
||||
id="arrow"
|
||||
viewBox="0 0 10 10"
|
||||
refX="20"
|
||||
refY="5"
|
||||
markerWidth="6"
|
||||
markerHeight="6"
|
||||
orient="auto"
|
||||
>
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" fill="var(--vp-c-divider)" />
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
<!-- 节点 -->
|
||||
<g
|
||||
v-for="(node, index) in nodes"
|
||||
:key="index"
|
||||
class="graph-node"
|
||||
@click="selectedNode = index"
|
||||
>
|
||||
<circle
|
||||
:cx="node.x"
|
||||
:cy="node.y"
|
||||
r="20"
|
||||
:fill="selectedNode === index ? 'var(--vp-c-brand)' : 'var(--vp-c-brand-soft)'"
|
||||
stroke="var(--vp-c-brand)"
|
||||
stroke-width="2"
|
||||
/>
|
||||
<text
|
||||
:x="node.x"
|
||||
:y="node.y"
|
||||
text-anchor="middle"
|
||||
dominant-baseline="middle"
|
||||
fill="white"
|
||||
font-size="12"
|
||||
font-weight="600"
|
||||
>
|
||||
{{ node.label }}
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div class="graph-info">
|
||||
<div class="info-title">图的特点</div>
|
||||
<div class="info-grid">
|
||||
<div class="info-item">
|
||||
<div class="item-label">节点 (V)</div>
|
||||
<div class="item-value">{{ nodes.length }}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="item-label">边 (E)</div>
|
||||
<div class="item-value">{{ edges.length }}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="item-label">度</div>
|
||||
<div class="item-value">{{ averageDegree }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="applications">
|
||||
<div class="app-title">应用场景</div>
|
||||
<div class="app-list">
|
||||
<div class="app-item">
|
||||
<span class="app-icon">🗺️</span>
|
||||
<span class="app-text">地图导航(最短路径)</span>
|
||||
</div>
|
||||
<div class="app-item">
|
||||
<span class="app-icon">👥</span>
|
||||
<span class="app-text">社交网络(好友关系)</span>
|
||||
</div>
|
||||
<div class="app-item">
|
||||
<span class="app-icon">🌐</span>
|
||||
<span class="app-text">网页链接(PageRank)</span>
|
||||
</div>
|
||||
<div class="app-item">
|
||||
<span class="app-icon">🔗</span>
|
||||
<span class="app-text">依赖关系(包管理)</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const graphType = ref('undirected')
|
||||
const selectedNode = ref(null)
|
||||
|
||||
const nodes = [
|
||||
{ label: 'A', x: 200, y: 50 },
|
||||
{ label: 'B', x: 100, y: 130 },
|
||||
{ label: 'C', x: 300, y: 130 },
|
||||
{ label: 'D', x: 100, y: 250 },
|
||||
{ label: 'E', x: 300, y: 250 }
|
||||
]
|
||||
|
||||
const edges = ref([
|
||||
{ id: 1, from: 0, to: 1 },
|
||||
{ id: 2, from: 0, to: 2 },
|
||||
{ id: 3, from: 1, to: 2 },
|
||||
{ id: 4, from: 1, to: 3 },
|
||||
{ id: 5, from: 2, to: 4 },
|
||||
{ id: 6, from: 3, to: 4 }
|
||||
])
|
||||
|
||||
const averageDegree = computed(() => {
|
||||
return (edges.value.length * 2 / nodes.length).toFixed(1)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.graph-structure-demo {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.demo-header .icon { font-size: 1.5rem; }
|
||||
.demo-header .title { font-weight: 700; font-size: 1.1rem; }
|
||||
.demo-header .subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
|
||||
|
||||
.graph-types {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.type-selector {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.type-btn {
|
||||
padding: 0.6rem 1.25rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
font-size: 0.9rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.type-btn.active {
|
||||
background: var(--vp-c-brand);
|
||||
border-color: var(--vp-c-brand);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.graph-visualization {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.graph-svg {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.graph-node {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.graph-node circle {
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.graph-node:hover circle {
|
||||
r: 25;
|
||||
}
|
||||
|
||||
.graph-info {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.info-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.info-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
text-align: center;
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.item-label {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.item-value {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.applications {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.app-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.app-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.app-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 0.75rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.app-icon {
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
.app-text {
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
</style>
|
||||
+560
@@ -0,0 +1,560 @@
|
||||
<template>
|
||||
<div class="greedy-thinking-demo">
|
||||
<div class="demo-header">
|
||||
<span class="icon">🎯</span>
|
||||
<span class="title">贪心算法:每步都选当前最优</span>
|
||||
<span class="subtitle">局部最优 → 全局最优?</span>
|
||||
</div>
|
||||
|
||||
<div class="core-idea">
|
||||
<div class="idea-box">
|
||||
<div class="idea-icon">💡</div>
|
||||
<div class="idea-text">
|
||||
贪心算法在每一步选择中都采取当前状态下<strong>最优</strong>的选择<br>
|
||||
希望通过一系列局部最优选择达到<strong>全局最优</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="scenario-selector">
|
||||
<div class="selector-title">经典问题</div>
|
||||
<div class="scenario-buttons">
|
||||
<button
|
||||
v-for="scenario in scenarios"
|
||||
:key="scenario.id"
|
||||
:class="['scenario-btn', { active: activeScenario === scenario.id }]"
|
||||
@click="activeScenario = scenario.id"
|
||||
>
|
||||
{{ scenario.icon }} {{ scenario.name }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 找零钱问题 -->
|
||||
<div v-if="activeScenario === 'change'" class="scenario-content">
|
||||
<div class="content-title">找零钱问题</div>
|
||||
<div class="change-demo">
|
||||
<div class="change-amount">
|
||||
需要找零:<span class="amount">{{ changeAmount }}</span> 元
|
||||
</div>
|
||||
<div class="change-process">
|
||||
<div class="process-step" v-for="(step, index) in changeSteps" :key="index">
|
||||
<div class="step-coin">{{ step.coin }}</div>
|
||||
<div class="step-text">× {{ step.count }} = {{ step.value }}元</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="change-result">
|
||||
共需要 <strong>{{ totalCoins }}</strong> 个硬币
|
||||
</div>
|
||||
</div>
|
||||
<div class="scenario-note">
|
||||
✓ 贪心策略:每次选择面值最大的硬币<br>
|
||||
✓ 适用于人民币、美元等货币系统
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 活动选择问题 -->
|
||||
<div v-if="activeScenario === 'activity'" class="scenario-content">
|
||||
<div class="content-title">活动选择问题</div>
|
||||
<div class="activity-demo">
|
||||
<div class="activities-list">
|
||||
<div
|
||||
v-for="(activity, index) in activities"
|
||||
:key="index"
|
||||
:class="['activity-item', { selected: activity.selected, conflicting: activity.conflicting }]"
|
||||
>
|
||||
<div class="activity-time">{{ activity.start }} - {{ activity.end }}</div>
|
||||
<div class="activity-name">{{ activity.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="activity-rule">
|
||||
贪心策略:<strong>选择最早结束</strong>的活动
|
||||
</div>
|
||||
<div class="activity-result">
|
||||
最多可以参加 <strong>{{ selectedCount }}</strong> 个活动
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 最短路径 -->
|
||||
<div v-if="activeScenario === 'shortest'" class="scenario-content">
|
||||
<div class="content-title">最短路径问题 (Dijkstra)</div>
|
||||
<div class="shortest-demo">
|
||||
<div class="path-graph">
|
||||
<div class="graph-nodes">
|
||||
<div class="node start">A(起点)</div>
|
||||
<div class="node">B</div>
|
||||
<div class="node">C</div>
|
||||
<div class="node">D</div>
|
||||
<div class="node end">E(终点)</div>
|
||||
</div>
|
||||
<div class="graph-edges">
|
||||
<div class="edge">A-B: 4</div>
|
||||
<div class="edge">A-C: 2</div>
|
||||
<div class="edge">B-D: 3</div>
|
||||
<div class="edge">C-D: 1</div>
|
||||
<div class="edge">C-E: 5</div>
|
||||
<div class="edge">D-E: 2</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="path-result">
|
||||
<div class="result-step">从 A 出发,选择距离最近的节点</div>
|
||||
<div class="result-path">A → C → D → E</div>
|
||||
<div class="result-distance">总距离:2 + 1 + 2 = 5</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 贪心 vs 动态规划 -->
|
||||
<div class="comparison">
|
||||
<div class="comparison-title">贪心 vs 动态规划</div>
|
||||
<div class="comparison-table">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>特点</th>
|
||||
<th>贪心算法</th>
|
||||
<th>动态规划</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>决策方式</td>
|
||||
<td>每步选当前最优</td>
|
||||
<td>考虑所有可能,选最优</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>最优性</td>
|
||||
<td>可能不是全局最优</td>
|
||||
<td>保证全局最优</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>时间复杂度</td>
|
||||
<td>O(n) 或 O(n log n)</td>
|
||||
<td>O(n²) 或更高</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>适用场景</td>
|
||||
<td>局部最优 → 全局最优</td>
|
||||
<td>重叠子问题</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 优缺点 -->
|
||||
<div class="pros-cons">
|
||||
<div class="pros-column">
|
||||
<div class="column-title">✓ 优点</div>
|
||||
<ul>
|
||||
<li>实现简单</li>
|
||||
<li>效率高</li>
|
||||
<li>空间复杂度低</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="cons-column">
|
||||
<div class="column-title">✗ 缺点</div>
|
||||
<ul>
|
||||
<li>不保证全局最优</li>
|
||||
<li>适用范围有限</li>
|
||||
<li>需要证明最优性</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const activeScenario = ref('change')
|
||||
|
||||
const scenarios = [
|
||||
{ id: 'change', name: '找零钱', icon: '💰' },
|
||||
{ id: 'activity', name: '活动选择', icon: '📅' },
|
||||
{ id: 'shortest', name: '最短路径', icon: '🗺️' }
|
||||
]
|
||||
|
||||
const changeAmount = ref(37)
|
||||
|
||||
const changeSteps = [
|
||||
{ coin: '20元', count: 1, value: 20 },
|
||||
{ coin: '10元', count: 1, value: 10 },
|
||||
{ coin: '5元', count: 1, value: 5 },
|
||||
{ coin: '1元', count: 2, value: 2 }
|
||||
]
|
||||
|
||||
const totalCoins = computed(() => changeSteps.reduce((sum, step) => sum + step.count, 0))
|
||||
|
||||
const activities = [
|
||||
{ start: '9:00', end: '10:00', name: '活动1', selected: true, conflicting: false },
|
||||
{ start: '9:30', end: '11:30', name: '活动2', selected: false, conflicting: true },
|
||||
{ start: '10:00', end: '11:00', name: '活动3', selected: true, conflicting: false },
|
||||
{ start: '10:30', end: '12:00', name: '活动4', selected: false, conflicting: true },
|
||||
{ start: '11:00', end: '12:00', name: '活动5', selected: true, conflicting: false }
|
||||
]
|
||||
|
||||
const selectedCount = computed(() => activities.filter(a => a.selected).length)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.greedy-thinking-demo {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.demo-header .icon { font-size: 1.5rem; }
|
||||
.demo-header .title { font-weight: 700; font-size: 1.1rem; }
|
||||
.demo-header .subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
|
||||
|
||||
.core-idea {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.idea-box {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
padding: 1.25rem;
|
||||
background: var(--vp-c-bg);
|
||||
border-left: 4px solid var(--vp-c-brand);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.idea-icon {
|
||||
font-size: 2rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.idea-text {
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.scenario-selector {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.selector-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 0.75rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.scenario-buttons {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.scenario-btn {
|
||||
padding: 0.6rem 1rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 0.9rem;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.scenario-btn:hover {
|
||||
border-color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.scenario-btn.active {
|
||||
background: var(--vp-c-brand);
|
||||
border-color: var(--vp-c-brand);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.scenario-content {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.content-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
text-align: center;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.change-demo {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.change-amount {
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.amount {
|
||||
color: var(--vp-c-brand);
|
||||
font-weight: 700;
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
.change-process {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.process-step {
|
||||
text-align: center;
|
||||
padding: 0.75rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.step-coin {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-brand);
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.step-text {
|
||||
font-size: 0.85rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.change-result {
|
||||
font-size: 1rem;
|
||||
padding: 0.75rem;
|
||||
background: rgba(16, 185, 129, 0.1);
|
||||
border: 1px solid #10b981;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.scenario-note {
|
||||
margin-top: 1rem;
|
||||
padding: 0.75rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 6px;
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.activities-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.activity-item {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
padding: 0.75rem;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.activity-item.selected {
|
||||
background: rgba(16, 185, 129, 0.1);
|
||||
border-color: #10b981;
|
||||
}
|
||||
|
||||
.activity-item.conflicting {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.activity-time {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-weight: 600;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.activity-name {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.activity-rule {
|
||||
text-align: center;
|
||||
padding: 0.75rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 6px;
|
||||
margin-bottom: 1rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.activity-result {
|
||||
text-align: center;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.shortest-demo {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.shortest-demo {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.path-graph {
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 6px;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.graph-nodes {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.node {
|
||||
padding: 0.5rem 0.75rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.node.start {
|
||||
background: rgba(16, 185, 129, 0.1);
|
||||
border-color: #10b981;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.node.end {
|
||||
background: rgba(59, 130, 246, 0.1);
|
||||
border-color: #3b82f6;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.graph-edges {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.edge {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.path-result {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.result-step {
|
||||
font-size: 0.9rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.result-path {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-brand);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.result-distance {
|
||||
text-align: center;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.comparison {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.comparison-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.comparison-table table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.comparison-table th {
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
padding: 0.75rem;
|
||||
text-align: center;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.comparison-table td {
|
||||
padding: 0.75rem;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
text-align: center;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.pros-cons {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.pros-cons {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.pros-column,
|
||||
.cons-column {
|
||||
padding: 1.25rem;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.pros-column {
|
||||
background: rgba(16, 185, 129, 0.1);
|
||||
border: 1px solid #10b981;
|
||||
}
|
||||
|
||||
.cons-column {
|
||||
background: rgba(239, 68, 68, 0.1);
|
||||
border: 1px solid #ef4444;
|
||||
}
|
||||
|
||||
.column-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.pros-column ul,
|
||||
.cons-column ul {
|
||||
margin: 0;
|
||||
padding-left: 1.25rem;
|
||||
}
|
||||
|
||||
.pros-column li,
|
||||
.cons-column li {
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.8;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,434 @@
|
||||
<template>
|
||||
<div class="hash-table-demo">
|
||||
<div class="demo-header">
|
||||
<span class="icon">🗂️</span>
|
||||
<span class="title">哈希表:超快的查找</span>
|
||||
<span class="subtitle">通过关键词直接找到数据</span>
|
||||
</div>
|
||||
|
||||
<div class="analogy-box">
|
||||
<div class="analogy-icon">📚</div>
|
||||
<div class="analogy-text">
|
||||
哈希表就像图书馆的<strong>索引卡片</strong>:不用在一排排书架上找,直接查索引就能找到书的位置
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hash-visual">
|
||||
<div class="input-section">
|
||||
<div class="section-title">存储数据</div>
|
||||
<div class="input-group">
|
||||
<input
|
||||
v-model="newKey"
|
||||
type="text"
|
||||
placeholder="键 (如: apple)"
|
||||
class="hash-input"
|
||||
/>
|
||||
<input
|
||||
v-model="newValue"
|
||||
type="text"
|
||||
placeholder="值 (如: 苹果)"
|
||||
class="hash-input"
|
||||
/>
|
||||
<button @click="addData" class="add-btn">
|
||||
添加
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hash-process">
|
||||
<div class="process-title">哈希过程</div>
|
||||
<div class="process-diagram">
|
||||
<div class="process-step">
|
||||
<div class="step-label">输入键</div>
|
||||
<div class="step-box">{{ exampleKey }}</div>
|
||||
</div>
|
||||
<div class="process-arrow">↓</div>
|
||||
<div class="process-step">
|
||||
<div class="step-label">哈希函数</div>
|
||||
<div class="step-box func">hash(key) % 10</div>
|
||||
</div>
|
||||
<div class="process-arrow">↓</div>
|
||||
<div class="process-step">
|
||||
<div class="step-label">数组索引</div>
|
||||
<div class="step-box index">{{ exampleIndex }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hash-table-display">
|
||||
<div class="section-title">哈希表</div>
|
||||
<div class="table-slots">
|
||||
<div
|
||||
v-for="(slot, index) in hashTable"
|
||||
:key="index"
|
||||
:class="['table-slot', { occupied: slot !== null, highlighted: index === exampleIndex }]"
|
||||
>
|
||||
<div class="slot-index">{{ index }}</div>
|
||||
<div class="slot-value">{{ slot || '空' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="performance-comparison">
|
||||
<div class="comparison-title">性能对比</div>
|
||||
<div class="comparison-grid">
|
||||
<div class="comparison-item">
|
||||
<div class="item-label">哈希表查找</div>
|
||||
<div class="item-value excellent">O(1)</div>
|
||||
<div class="item-desc">瞬间找到</div>
|
||||
</div>
|
||||
<div class="comparison-item">
|
||||
<div class="item-label">数组查找</div>
|
||||
<div class="item-value good">O(n)</div>
|
||||
<div class="item-desc">需要遍历</div>
|
||||
</div>
|
||||
<div class="comparison-item">
|
||||
<div class="item-label">二分查找</div>
|
||||
<div class="item-value better">O(log n)</div>
|
||||
<div class="item-desc">需要排序</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="applications">
|
||||
<div class="app-title">常见应用</div>
|
||||
<div class="app-list">
|
||||
<div class="app-item">
|
||||
<span class="app-icon">👤</span>
|
||||
<div class="app-text">用户信息表(用户ID → 用户资料)</div>
|
||||
</div>
|
||||
<div class="app-item">
|
||||
<span class="app-icon">🛒</span>
|
||||
<div class="app-text">购物车(商品ID → 数量)</div>
|
||||
</div>
|
||||
<div class="app-item">
|
||||
<span class="app-icon">📝</span>
|
||||
<div class="app-text">缓存系统(URL → 网页内容)</div>
|
||||
</div>
|
||||
<div class="app-item">
|
||||
<span class="app-icon">🔍</span>
|
||||
<div class="app-text">字典(单词 → 释义)</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const newKey = ref('')
|
||||
const newValue = ref('')
|
||||
const exampleKey = ref('apple')
|
||||
|
||||
const hashTable = ref([null, null, null, null, null, null, null, null, null, null])
|
||||
|
||||
// 初始化一些数据
|
||||
const initData = () => {
|
||||
const data = [
|
||||
{ key: 'apple', value: '苹果' },
|
||||
{ key: 'banana', value: '香蕉' },
|
||||
{ key: 'orange', value: '橙子' }
|
||||
]
|
||||
data.forEach(item => {
|
||||
const index = simpleHash(item.key)
|
||||
hashTable.value[index] = `${item.key}: ${item.value}`
|
||||
})
|
||||
}
|
||||
|
||||
const simpleHash = (key) => {
|
||||
let hash = 0
|
||||
for (let i = 0; i < key.length; i++) {
|
||||
hash += key.charCodeAt(i)
|
||||
}
|
||||
return hash % 10
|
||||
}
|
||||
|
||||
const exampleIndex = computed(() => simpleHash(exampleKey.value))
|
||||
|
||||
const addData = () => {
|
||||
if (newKey.value && newValue.value) {
|
||||
const index = simpleHash(newKey.value)
|
||||
hashTable.value[index] = `${newKey.value}: ${newValue.value}`
|
||||
newKey.value = ''
|
||||
newValue.value = ''
|
||||
}
|
||||
}
|
||||
|
||||
initData()
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.hash-table-demo {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.demo-header .icon { font-size: 1.5rem; }
|
||||
.demo-header .title { font-weight: 700; font-size: 1.1rem; }
|
||||
.demo-header .subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
|
||||
|
||||
.analogy-box {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg);
|
||||
border-left: 4px solid var(--vp-c-brand);
|
||||
border-radius: 6px;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.analogy-icon {
|
||||
font-size: 2rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.analogy-text {
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.hash-visual {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.hash-visual {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.input-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.hash-input {
|
||||
padding: 0.75rem;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.add-btn {
|
||||
padding: 0.75rem;
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.add-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.process-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.process-diagram {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.process-step {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.step-label {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.step-box {
|
||||
padding: 0.75rem 1.5rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.step-box.func {
|
||||
background: var(--vp-c-brand-soft);
|
||||
border-color: var(--vp-c-brand);
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.step-box.index {
|
||||
background: #10b981;
|
||||
border-color: #10b981;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.process-arrow {
|
||||
font-size: 1.5rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.hash-table-display {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.table-slots {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.table-slot {
|
||||
text-align: center;
|
||||
padding: 0.75rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.table-slot.occupied {
|
||||
background: var(--vp-c-brand-soft);
|
||||
border-color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.table-slot.highlighted {
|
||||
border-color: #10b981;
|
||||
background: rgba(16, 185, 129, 0.1);
|
||||
}
|
||||
|
||||
.slot-index {
|
||||
font-size: 0.75rem;
|
||||
color: var(--vp-c-text-2);
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.slot-value {
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.performance-comparison {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.comparison-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.comparison-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.comparison-item {
|
||||
text-align: center;
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.item-label {
|
||||
font-size: 0.85rem;
|
||||
color: var(--vp-c-text-2);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.item-value {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.item-value.excellent {
|
||||
color: #10b981;
|
||||
}
|
||||
|
||||
.item-value.good {
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.item-value.better {
|
||||
color: #f59e0b;
|
||||
}
|
||||
|
||||
.item-desc {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.applications {
|
||||
border-top: 1px solid var(--vp-c-divider);
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
|
||||
.app-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.app-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.app-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 0.75rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.app-icon {
|
||||
font-size: 1.5rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.app-text {
|
||||
font-size: 0.85rem;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
</style>
|
||||
+514
@@ -0,0 +1,514 @@
|
||||
<template>
|
||||
<div class="language-evolution-demo">
|
||||
<div class="demo-header">
|
||||
<span class="icon">📜</span>
|
||||
<span class="title">编程语言的演化</span>
|
||||
<span class="subtitle">从机器语言到高级语言</span>
|
||||
</div>
|
||||
|
||||
<div class="evolution-timeline">
|
||||
<div class="timeline-track">
|
||||
<div
|
||||
v-for="(era, index) in eras"
|
||||
:key="index"
|
||||
:class="['era-marker', { active: activeEra === index }]"
|
||||
:style="{ left: era.position }"
|
||||
@click="activeEra = index"
|
||||
>
|
||||
<div class="marker-dot"></div>
|
||||
<div class="marker-label">{{ era.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="era-detail">
|
||||
<div class="detail-header">
|
||||
<span class="detail-icon">{{ currentEra.icon }}</span>
|
||||
<span class="detail-title">{{ currentEra.fullname }}</span>
|
||||
<span class="detail-years">{{ currentEra.years }}</span>
|
||||
</div>
|
||||
|
||||
<div class="detail-content">
|
||||
<div class="content-section">
|
||||
<div class="section-title">代码示例</div>
|
||||
<div class="code-example">
|
||||
<pre><code>{{ currentEra.example }}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content-section">
|
||||
<div class="section-title">特点</div>
|
||||
<div class="features-list">
|
||||
<div
|
||||
v-for="(feature, index) in currentEra.features"
|
||||
:key="index"
|
||||
class="feature-item"
|
||||
>
|
||||
<span class="feature-icon">✓</span>
|
||||
<span class="feature-text">{{ feature }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content-section">
|
||||
<div class="section-title">优缺点</div>
|
||||
<div class="pros-cons">
|
||||
<div class="pros">
|
||||
<div class="list-title">✓ 优点</div>
|
||||
<ul>
|
||||
<li v-for="(pro, index) in currentEra.pros" :key="index">{{ pro }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="cons">
|
||||
<div class="list-title">✗ 缺点</div>
|
||||
<ul>
|
||||
<li v-for="(con, index) in currentEra.cons" :key="index">{{ con }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 演化总结 -->
|
||||
<div class="evolution-summary">
|
||||
<div class="summary-title">演化的趋势</div>
|
||||
<div class="trend-grid">
|
||||
<div class="trend-card">
|
||||
<div class="trend-icon">🚀</div>
|
||||
<div class="trend-title">越来越抽象</div>
|
||||
<div class="trend-desc">远离硬件细节,更接近人类思维</div>
|
||||
</div>
|
||||
<div class="trend-card">
|
||||
<div class="trend-icon">👥</div>
|
||||
<div class="trend-title">越来越易用</div>
|
||||
<div class="trend-desc">语法更简洁,学习曲线更平缓</div>
|
||||
</div>
|
||||
<div class="trend-card">
|
||||
<div class="trend-icon">🛡️</div>
|
||||
<div class="trend-title">越来越安全</div>
|
||||
<div class="trend-desc">类型系统、内存管理等安全机制</div>
|
||||
</div>
|
||||
<div class="trend-card">
|
||||
<div class="trend-icon">⚡</div>
|
||||
<div class="trend-title">越来越高效</div>
|
||||
<div class="trend-desc">编译器优化、JIT 技术提升性能</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 现代语言生态 -->
|
||||
<div class="modern-languages">
|
||||
<div class="modern-title">现代编程语言生态</div>
|
||||
<div class="language-grid">
|
||||
<div
|
||||
v-for="lang in modernLanguages"
|
||||
:key="lang.name"
|
||||
class="lang-card"
|
||||
>
|
||||
<div class="lang-name">{{ lang.name }}</div>
|
||||
<div class="lang-year">{{ lang.year }}</div>
|
||||
<div class="lang-uses">
|
||||
<span
|
||||
v-for="(use, index) in lang.uses"
|
||||
:key="index"
|
||||
class="use-tag"
|
||||
>
|
||||
{{ use }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const activeEra = ref(3)
|
||||
|
||||
const eras = [
|
||||
{
|
||||
name: '机器语言',
|
||||
fullname: '机器语言时代',
|
||||
years: '1940s - 1950s',
|
||||
icon: '0️⃣',
|
||||
position: '5%',
|
||||
example: '10110000 11000000\n(add two numbers)',
|
||||
features: [
|
||||
'直接用二进制代码',
|
||||
'机器可以直接执行',
|
||||
'完全依赖硬件'
|
||||
],
|
||||
pros: ['执行速度最快', '直接控制硬件'],
|
||||
cons: ['极难编写', '容易出错', '不可移植']
|
||||
},
|
||||
{
|
||||
name: '汇编语言',
|
||||
fullname: '汇编语言时代',
|
||||
years: '1950s - 1960s',
|
||||
icon: '🔧',
|
||||
position: '25%',
|
||||
example: 'MOV AX, 5\nADD AX, 3\n(add 5 and 3)',
|
||||
features: [
|
||||
'用助记符代替二进制',
|
||||
'需要汇编器翻译',
|
||||
'仍然依赖硬件'
|
||||
],
|
||||
pros: ['比机器语言好懂', '效率仍然很高'],
|
||||
cons: ['代码冗长', '不可移植', '需要了解硬件']
|
||||
},
|
||||
{
|
||||
name: '面向过程',
|
||||
fullname: '面向过程语言',
|
||||
years: '1970s - 1980s',
|
||||
icon: '📋',
|
||||
position: '50%',
|
||||
example: 'int add(int a, int b) {\n return a + b;\n}',
|
||||
features: [
|
||||
'函数、变量等抽象',
|
||||
'结构化编程',
|
||||
'可移植性好'
|
||||
],
|
||||
pros: ['易读易写', '可移植', '效率较高'],
|
||||
cons: ['大型项目难以维护', '代码重用性差']
|
||||
},
|
||||
{
|
||||
name: '面向对象',
|
||||
fullname: '面向对象语言',
|
||||
years: '1990s - 2000s',
|
||||
icon: '🎯',
|
||||
position: '75%',
|
||||
example: 'class Calculator {\n add(a, b) { return a + b; }\n}',
|
||||
features: [
|
||||
'类、对象、封装、继承',
|
||||
'模块化设计',
|
||||
'代码复用性强'
|
||||
],
|
||||
pros: ['适合大型项目', '易维护', '可扩展'],
|
||||
cons: ['学习曲线陡', '代码量较大']
|
||||
},
|
||||
{
|
||||
name: '现代语言',
|
||||
fullname: '现代多范式语言',
|
||||
years: '2010s - 现在',
|
||||
icon: '🚀',
|
||||
position: '95%',
|
||||
example: 'const add = (a, b) => a + b;\n(add arrow function)',
|
||||
features: [
|
||||
'简洁优雅的语法',
|
||||
'多范式支持',
|
||||
'强大的标准库'
|
||||
],
|
||||
pros: ['开发效率高', '生态丰富', '社区活跃'],
|
||||
cons: ['抽象层多', '性能可能不如底层语言']
|
||||
}
|
||||
]
|
||||
|
||||
const modernLanguages = [
|
||||
{ name: 'Python', year: '1991', uses: ['AI/ML', '数据分析', 'Web'] },
|
||||
{ name: 'JavaScript', year: '1995', uses: ['Web', 'Node.js', '前端'] },
|
||||
{ name: 'Rust', year: '2010', uses: ['系统', 'WebAssembly', '性能'] },
|
||||
{ name: 'Go', year: '2009', uses: ['后端', '云', '微服务'] },
|
||||
{ name: 'TypeScript', year: '2012', uses: ['Web', '大型项目'] },
|
||||
{ name: 'Swift', year: '2014', uses: ['iOS', 'macOS'] }
|
||||
]
|
||||
|
||||
const currentEra = computed(() => eras[activeEra.value])
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.language-evolution-demo {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.demo-header .icon { font-size: 1.5rem; }
|
||||
.demo-header .title { font-weight: 700; font-size: 1.1rem; }
|
||||
.demo-header .subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
|
||||
|
||||
.evolution-timeline {
|
||||
background: var(--vp-c-bg);
|
||||
border-radius: 8px;
|
||||
padding: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.timeline-track {
|
||||
position: relative;
|
||||
height: 60px;
|
||||
border-top: 3px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.era-marker {
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
transform: translateX(-50%);
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.marker-dot {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: var(--vp-c-divider);
|
||||
border: 3px solid var(--vp-c-bg);
|
||||
border-radius: 50%;
|
||||
margin: 0 auto 0.5rem;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.era-marker:hover .marker-dot,
|
||||
.era-marker.active .marker-dot {
|
||||
background: var(--vp-c-brand);
|
||||
transform: scale(1.3);
|
||||
}
|
||||
|
||||
.marker-label {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.era-marker.active .marker-label {
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.era-detail {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.detail-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 1.5rem;
|
||||
padding-bottom: 1rem;
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.detail-icon {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.detail-title {
|
||||
font-weight: 600;
|
||||
font-size: 1.1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.detail-years {
|
||||
margin-left: auto;
|
||||
font-size: 0.85rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.detail-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.content-section {}
|
||||
|
||||
.section-title {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 0.75rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.code-example {
|
||||
background: #1e1e1e;
|
||||
border-radius: 6px;
|
||||
padding: 1rem;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.code-example pre {
|
||||
margin: 0;
|
||||
color: #d4d4d4;
|
||||
font-size: 0.8rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.features-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.feature-item {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.feature-icon {
|
||||
color: #10b981;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.feature-text {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.pros-cons {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.pros-cons {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.pros,
|
||||
.cons {
|
||||
padding: 1rem;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.pros {
|
||||
background: rgba(16, 185, 129, 0.1);
|
||||
border: 1px solid #10b981;
|
||||
}
|
||||
|
||||
.cons {
|
||||
background: rgba(239, 68, 68, 0.1);
|
||||
border: 1px solid #ef4444;
|
||||
}
|
||||
|
||||
.list-title {
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.pros ul,
|
||||
.cons ul {
|
||||
margin: 0;
|
||||
padding-left: 1.25rem;
|
||||
}
|
||||
|
||||
.pros li,
|
||||
.cons li {
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.evolution-summary {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.summary-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.trend-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.trend-card {
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.trend-icon {
|
||||
font-size: 1.8rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.trend-title {
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.trend-desc {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.modern-languages {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.modern-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.language-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.lang-card {
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.lang-name {
|
||||
font-weight: 700;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 0.35rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.lang-year {
|
||||
font-size: 0.75rem;
|
||||
color: var(--vp-c-text-2);
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.lang-uses {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.35rem;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.use-tag {
|
||||
padding: 0.25rem 0.5rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 10px;
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
</style>
|
||||
+505
@@ -0,0 +1,505 @@
|
||||
<template>
|
||||
<div class="language-scenario-demo">
|
||||
<div class="demo-header">
|
||||
<span class="icon">🎬</span>
|
||||
<span class="title">为什么需要编程语言?</span>
|
||||
<span class="subtitle">从场景看编程语言的价值</span>
|
||||
</div>
|
||||
|
||||
<div class="scenario-intro">
|
||||
编程语言是<strong>人类思维</strong>和<strong>计算机执行</strong>之间的桥梁
|
||||
</div>
|
||||
|
||||
<div class="scenario-cards">
|
||||
<div
|
||||
v-for="scenario in scenarios"
|
||||
:key="scenario.id"
|
||||
:class="['scenario-card', { active: activeScenario === scenario.id }]"
|
||||
@click="activeScenario = scenario.id"
|
||||
>
|
||||
<div class="card-icon">{{ scenario.icon }}</div>
|
||||
<div class="card-title">{{ scenario.title }}</div>
|
||||
<div class="card-desc">{{ scenario.desc }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 场景详解 -->
|
||||
<div v-if="activeScenario" class="scenario-detail">
|
||||
<div class="detail-header">
|
||||
<span class="detail-icon">{{ currentScenario.icon }}</span>
|
||||
<span class="detail-title">{{ currentScenario.title }}</span>
|
||||
</div>
|
||||
|
||||
<div class="detail-content">
|
||||
<div class="detail-section">
|
||||
<div class="section-title">场景描述</div>
|
||||
<div class="section-text">{{ currentScenario.fullDesc }}</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-section">
|
||||
<div class="section-title">为什么需要编程语言?</div>
|
||||
<div class="reason-steps">
|
||||
<div
|
||||
v-for="(step, index) in currentScenario.reasons"
|
||||
:key="index"
|
||||
class="reason-step"
|
||||
>
|
||||
<div class="step-number">{{ index + 1 }}</div>
|
||||
<div class="step-text">{{ step }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-section">
|
||||
<div class="section-title">不用编程语言会怎样?</div>
|
||||
<div class="without-code">
|
||||
<div class="without-box">
|
||||
<div class="without-icon">😰</div>
|
||||
<div class="without-text">{{ currentScenario.withoutLang }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-section">
|
||||
<div class="section-title">适合的语言</div>
|
||||
<div class="lang-tags">
|
||||
<span
|
||||
v-for="(lang, index) in currentScenario.languages"
|
||||
:key="index"
|
||||
class="lang-tag"
|
||||
>
|
||||
{{ lang }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 编程语言的作用 -->
|
||||
<div class="language-role">
|
||||
<div class="role-title">编程语言的三大作用</div>
|
||||
<div class="role-grid">
|
||||
<div class="role-card">
|
||||
<div class="role-icon">💬</div>
|
||||
<div class="role-title">表达思想</div>
|
||||
<div class="role-desc">将人类思维转化为计算机可理解的指令</div>
|
||||
</div>
|
||||
<div class="role-card">
|
||||
<div class="role-icon">🔧</div>
|
||||
<div class="role-title">控制硬件</div>
|
||||
<div class="role-desc">精确控制计算机执行各种操作</div>
|
||||
</div>
|
||||
<div class="role-card">
|
||||
<div class="role-icon">🤝</div>
|
||||
<div class="role-title">团队协作</div>
|
||||
<div class="role-desc">标准化的语法便于多人协作开发</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 演化历程 -->
|
||||
<div class="evolution">
|
||||
<div class="evolution-title">从机器码到高级语言</div>
|
||||
<div class="evolution-steps">
|
||||
<div class="evo-step">
|
||||
<div class="evo-level">低级</div>
|
||||
<div class="evo-name">机器语言</div>
|
||||
<div class="evo-arrow">↓</div>
|
||||
</div>
|
||||
<div class="evo-step">
|
||||
<div class="evo-level">低级</div>
|
||||
<div class="evo-name">汇编语言</div>
|
||||
<div class="evo-arrow">↓</div>
|
||||
</div>
|
||||
<div class="evo-step">
|
||||
<div class="evo-level">中级</div>
|
||||
<div class="evo-name">C 语言</div>
|
||||
<div class="evo-arrow">↓</div>
|
||||
</div>
|
||||
<div class="evo-step">
|
||||
<div class="evo-level">高级</div>
|
||||
<div class="evo-name">现代语言</div>
|
||||
<div class="evo-arrow">→</div>
|
||||
</div>
|
||||
<div class="evo-result">
|
||||
越来越接近<br>人类思维
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const activeScenario = ref('web')
|
||||
|
||||
const scenarios = [
|
||||
{
|
||||
id: 'web',
|
||||
icon: '🌐',
|
||||
title: '开发网站',
|
||||
desc: '创建交互式网页',
|
||||
fullDesc: '你需要创建一个在线购物网站,用户可以浏览商品、加入购物车、下单支付',
|
||||
reasons: [
|
||||
'HTML 定义网页结构',
|
||||
'CSS 实现美观样式',
|
||||
'JavaScript 实现交互功能',
|
||||
'Python/Node.js 处理后端逻辑'
|
||||
],
|
||||
withoutLang: '只能手工编写网页,无法实现动态内容和用户交互',
|
||||
languages: ['JavaScript', 'HTML', 'CSS', 'Python', 'TypeScript']
|
||||
},
|
||||
{
|
||||
id: 'mobile',
|
||||
icon: '📱',
|
||||
title: '开发 App',
|
||||
desc: '创建手机应用',
|
||||
fullDesc: '开发一个功能丰富的手机应用,支持 iOS 和 Android 平台',
|
||||
reasons: [
|
||||
'Swift/Kotlin 提供原生体验',
|
||||
'React Native 实现跨平台',
|
||||
'编程语言调用设备功能',
|
||||
'管理应用状态和数据'
|
||||
],
|
||||
withoutLang: '无法创建手机应用,只能使用系统自带的功能',
|
||||
languages: ['Swift', 'Kotlin', 'React Native', 'Flutter']
|
||||
},
|
||||
{
|
||||
id: 'data',
|
||||
icon: '📊',
|
||||
title: '数据分析',
|
||||
desc: '处理和分析大量数据',
|
||||
fullDesc: '分析百万级用户数据,找出行为模式和趋势',
|
||||
reasons: [
|
||||
'Python 提供丰富的数据科学库',
|
||||
'简洁的语法便于快速迭代',
|
||||
'强大的数据处理能力',
|
||||
'可视化工具支持'
|
||||
],
|
||||
withoutLang: '手工计算几乎不可能,需要几天才能完成分析',
|
||||
languages: ['Python', 'R', 'SQL', 'Julia']
|
||||
},
|
||||
{
|
||||
id: 'system',
|
||||
icon: '⚙️',
|
||||
title: '系统编程',
|
||||
desc: '编写操作系统和驱动',
|
||||
fullDesc: '开发操作系统内核、设备驱动等底层软件',
|
||||
reasons: [
|
||||
'C/C++ 提供底层访问能力',
|
||||
'精确控制内存管理',
|
||||
'高效的执行性能',
|
||||
'直接操作硬件'
|
||||
],
|
||||
withoutLang: '无法控制系统硬件,只能使用现有的操作系统功能',
|
||||
languages: ['C', 'C++', 'Rust', 'Assembly']
|
||||
},
|
||||
{
|
||||
id: 'ai',
|
||||
icon: '🤖',
|
||||
title: '人工智能',
|
||||
desc: '训练机器学习模型',
|
||||
fullDesc: '构建深度学习模型,识别图像、理解自然语言',
|
||||
reasons: [
|
||||
'Python 拥有丰富的 AI 框架',
|
||||
'简洁的数学表达',
|
||||
'GPU 加速支持',
|
||||
'庞大的社区支持'
|
||||
],
|
||||
withoutLang: '无法实现复杂的 AI 算法,只能使用简单的规则',
|
||||
languages: ['Python', 'R', 'Julia', 'C++']
|
||||
}
|
||||
]
|
||||
|
||||
const currentScenario = computed(() => scenarios.find(s => s.id === activeScenario.value))
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.language-scenario-demo {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.demo-header .icon { font-size: 1.5rem; }
|
||||
.demo-header .title { font-weight: 700; font-size: 1.1rem; }
|
||||
.demo-header .subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
|
||||
|
||||
.scenario-intro {
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg);
|
||||
border-left: 4px solid var(--vp-c-brand);
|
||||
border-radius: 6px;
|
||||
margin-bottom: 2rem;
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.scenario-cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||
gap: 1rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.scenario-card {
|
||||
padding: 1.25rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.scenario-card:hover {
|
||||
border-color: var(--vp-c-brand);
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
|
||||
.scenario-card.active {
|
||||
border-color: var(--vp-c-brand);
|
||||
background: var(--vp-c-brand-soft);
|
||||
}
|
||||
|
||||
.card-icon {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.card-desc {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.scenario-detail {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.detail-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 1.5rem;
|
||||
padding-bottom: 1rem;
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.detail-icon {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.detail-title {
|
||||
font-weight: 600;
|
||||
font-size: 1.1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.detail-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.detail-section {}
|
||||
|
||||
.section-title {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 0.75rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.section-text {
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.reason-steps {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.reason-step {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.step-number {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.step-text {
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.5;
|
||||
padding-top: 0.15rem;
|
||||
}
|
||||
|
||||
.without-code {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.without-box {
|
||||
display: inline-flex;
|
||||
gap: 0.75rem;
|
||||
align-items: center;
|
||||
padding: 0.75rem 1rem;
|
||||
background: rgba(239, 68, 68, 0.1);
|
||||
border: 1px solid #ef4444;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.without-icon {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.without-text {
|
||||
font-size: 0.85rem;
|
||||
color: #ef4444;
|
||||
}
|
||||
|
||||
.lang-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.lang-tag {
|
||||
padding: 0.5rem 0.75rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.language-role {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.role-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.role-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.role-card {
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.role-icon {
|
||||
font-size: 1.8rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.role-title {
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.role-desc {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.evolution {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.evolution-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.evolution-steps {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 1.5rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.evo-step {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.evo-level {
|
||||
font-size: 0.75rem;
|
||||
color: var(--vp-c-text-2);
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.evo-name {
|
||||
padding: 0.5rem 1rem;
|
||||
background: var(--vp-c-brand-soft);
|
||||
border: 1px solid var(--vp-c-brand);
|
||||
border-radius: 6px;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.evo-arrow {
|
||||
font-size: 1.2rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.evo-result {
|
||||
padding: 0.75rem 1rem;
|
||||
background: #10b981;
|
||||
color: white;
|
||||
border-radius: 6px;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
+255
@@ -0,0 +1,255 @@
|
||||
<template>
|
||||
<div class="language-type-model-demo">
|
||||
<div class="demo-header">
|
||||
<span class="icon">🏗️</span>
|
||||
<span class="title">编程语言的类型模型</span>
|
||||
<span class="subtitle">不同语言的类型系统差异</span>
|
||||
</div>
|
||||
|
||||
<div class="dimension-grid">
|
||||
<div
|
||||
v-for="dim in dimensions"
|
||||
:key="dim.id"
|
||||
class="dimension-card"
|
||||
>
|
||||
<div class="card-title">{{ dim.title }}</div>
|
||||
<div class="card-options">
|
||||
<div
|
||||
v-for="opt in dim.options"
|
||||
:key="opt.name"
|
||||
class="option-item"
|
||||
>
|
||||
<div class="option-name">{{ opt.name }}</div>
|
||||
<div class="option-langs">{{ opt.langs }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="quadrant-matrix">
|
||||
<div class="matrix-title">类型系统分类矩阵</div>
|
||||
<div class="matrix-grid">
|
||||
<div class="matrix-cell">
|
||||
<div class="cell-title">静态 + 强</div>
|
||||
<div class="cell-langs">Java, C++, Rust, Go</div>
|
||||
<div class="cell-desc">编译期检查,类型安全</div>
|
||||
</div>
|
||||
<div class="matrix-cell">
|
||||
<div class="cell-title">静态 + 弱</div>
|
||||
<div class="cell-langs">C</div>
|
||||
<div class="cell-desc">编译期检查,可随意转换</div>
|
||||
</div>
|
||||
<div class="matrix-cell">
|
||||
<div class="cell-title">动态 + 强</div>
|
||||
<div class="cell-langs">Python, Ruby</div>
|
||||
<div class="cell-desc">运行时检查,类型安全</div>
|
||||
</div>
|
||||
<div class="matrix-cell">
|
||||
<div class="cell-title">动态 + 弱</div>
|
||||
<div class="cell-langs">JavaScript, PHP</div>
|
||||
<div class="cell-desc">运行时检查,类型灵活</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="type-inference">
|
||||
<div class="inference-title">类型推断</div>
|
||||
<div class="inference-content">
|
||||
<div class="inference-desc">
|
||||
现代语言可以自动推断变量类型,无需显式声明
|
||||
</div>
|
||||
<div class="inference-examples">
|
||||
<div class="example-lang">
|
||||
<div class="lang-header">TypeScript</div>
|
||||
<pre><code>let x = 5; // 推断为 number
|
||||
let name = "Alice"; // string</code></pre>
|
||||
</div>
|
||||
<div class="example-lang">
|
||||
<div class="lang-header">Rust</div>
|
||||
<pre><code>let x = 5; // 推断为 i32
|
||||
let name = "Alice"; // &str</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const dimensions = [
|
||||
{
|
||||
id: 'static',
|
||||
title: '类型检查时机',
|
||||
options: [
|
||||
{ name: '静态类型', langs: 'Java, C++, Rust, Go' },
|
||||
{ name: '动态类型', langs: 'Python, JavaScript, Ruby' }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'strength',
|
||||
title: '类型强度',
|
||||
options: [
|
||||
{ name: '强类型', langs: 'Python, Java, Rust' },
|
||||
{ name: '弱类型', langs: 'JavaScript, C, PHP' }
|
||||
]
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.language-type-model-demo {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.demo-header .icon { font-size: 1.5rem; }
|
||||
.demo-header .title { font-weight: 700; font-size: 1.1rem; }
|
||||
.demo-header .subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
|
||||
|
||||
.dimension-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.dimension-card {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.card-options {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.option-item {
|
||||
padding: 0.75rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.option-name {
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.option-langs {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.quadrant-matrix {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.matrix-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.matrix-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.matrix-cell {
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.cell-title {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.cell-langs {
|
||||
font-size: 0.85rem;
|
||||
color: var(--vp-c-brand);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.cell-desc {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.type-inference {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.inference-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.inference-desc {
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 1rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.inference-examples {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.example-lang {
|
||||
background: #1e1e1e;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.lang-header {
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
padding: 0.5rem;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.example-lang pre {
|
||||
margin: 0;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.example-lang code {
|
||||
color: #d4d4d4;
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
</style>
|
||||
+579
@@ -0,0 +1,579 @@
|
||||
<template>
|
||||
<div class="linear-structures-demo">
|
||||
<div class="demo-header">
|
||||
<span class="icon">📚</span>
|
||||
<span class="title">线性结构的四种形态</span>
|
||||
<span class="subtitle">数组、链表、栈、队列的区别</span>
|
||||
</div>
|
||||
|
||||
<div class="structure-tabs">
|
||||
<button
|
||||
v-for="structure in structures"
|
||||
:key="structure.id"
|
||||
:class="['tab-btn', { active: activeStructure === structure.id }]"
|
||||
@click="activeStructure = structure.id"
|
||||
>
|
||||
{{ structure.icon }} {{ structure.name }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 可视化展示 -->
|
||||
<div class="structure-visual">
|
||||
<div class="visual-header">
|
||||
<span class="structure-title">{{ currentStructure.name }}</span>
|
||||
<span class="structure-tagline">{{ currentStructure.tagline }}</span>
|
||||
</div>
|
||||
|
||||
<!-- 数组 -->
|
||||
<div v-if="activeStructure === 'array'" class="array-visual">
|
||||
<div class="memory-block">
|
||||
<div
|
||||
v-for="(item, index) in arrayData"
|
||||
:key="index"
|
||||
class="array-cell"
|
||||
>
|
||||
<div class="cell-index">{{ index }}</div>
|
||||
<div class="cell-value">{{ item }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="visual-note">
|
||||
✓ 连续内存存储 | ✓ 快速访问 (O(1)) | ✗ 插入删除慢 (O(n))
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 链表 -->
|
||||
<div v-if="activeStructure === 'linkedlist'" class="linkedlist-visual">
|
||||
<div class="nodes-chain">
|
||||
<div
|
||||
v-for="(item, index) in linkedListData"
|
||||
:key="index"
|
||||
class="linked-node"
|
||||
>
|
||||
<div class="node-data">{{ item }}</div>
|
||||
<div class="node-pointer">→</div>
|
||||
</div>
|
||||
<div class="linked-node null">
|
||||
<div class="node-data">NULL</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="visual-note">
|
||||
✓ 非连续内存 | ✗ 访问慢 (O(n)) | ✓ 快速插入删除
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 栈 -->
|
||||
<div v-if="activeStructure === 'stack'" class="stack-visual">
|
||||
<div class="stack-container">
|
||||
<div class="stack-top">栈顶 ↓</div>
|
||||
<div class="stack-items">
|
||||
<div
|
||||
v-for="(item, index) in stackData"
|
||||
:key="index"
|
||||
class="stack-item"
|
||||
>
|
||||
{{ item }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="stack-bottom">栈底</div>
|
||||
</div>
|
||||
<div class="stack-operations">
|
||||
<button class="op-btn" @click="pushStack">
|
||||
入栈 (PUSH)
|
||||
</button>
|
||||
<button class="op-btn" @click="popStack">
|
||||
出栈 (POP)
|
||||
</button>
|
||||
</div>
|
||||
<div class="visual-note">
|
||||
后进先出 (LIFO) | 应用:撤销操作、函数调用
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 队列 -->
|
||||
<div v-if="activeStructure === 'queue'" class="queue-visual">
|
||||
<div class="queue-container">
|
||||
<div class="queue-front">队首 →</div>
|
||||
<div class="queue-items">
|
||||
<div
|
||||
v-for="(item, index) in queueData"
|
||||
:key="index"
|
||||
class="queue-item"
|
||||
>
|
||||
{{ item }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="queue-rear">→ 队尾</div>
|
||||
</div>
|
||||
<div class="queue-operations">
|
||||
<button class="op-btn" @click="enqueue">
|
||||
入队 (ENQUEUE)
|
||||
</button>
|
||||
<button class="op-btn" @click="dequeue">
|
||||
出队 (DEQUEUE)
|
||||
</button>
|
||||
</div>
|
||||
<div class="visual-note">
|
||||
先进先出 (FIFO) | 应用:任务队列、打印队列
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 对比表格 -->
|
||||
<div class="comparison-table">
|
||||
<div class="table-title">操作对比</div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>数据结构</th>
|
||||
<th>访问</th>
|
||||
<th>插入</th>
|
||||
<th>删除</th>
|
||||
<th>特点</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="struct in structures"
|
||||
:key="struct.id"
|
||||
:class="{ highlighted: struct.id === activeStructure }"
|
||||
>
|
||||
<td>{{ struct.icon }} {{ struct.name }}</td>
|
||||
<td>{{ struct.access }}</td>
|
||||
<td>{{ struct.insert }}</td>
|
||||
<td>{{ struct.delete }}</td>
|
||||
<td>{{ struct.feature }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- 应用场景 -->
|
||||
<div class="applications">
|
||||
<div class="app-title">实际应用场景</div>
|
||||
<div class="app-list">
|
||||
<div
|
||||
v-for="(app, index) in currentStructure.applications"
|
||||
:key="index"
|
||||
class="app-card"
|
||||
>
|
||||
<div class="app-icon">{{ app.icon }}</div>
|
||||
<div class="app-content">
|
||||
<div class="app-name">{{ app.name }}</div>
|
||||
<div class="app-desc">{{ app.desc }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const activeStructure = ref('array')
|
||||
|
||||
const structures = [
|
||||
{
|
||||
id: 'array',
|
||||
name: '数组',
|
||||
icon: '📊',
|
||||
tagline: '连续内存,编号访问',
|
||||
access: 'O(1) 快',
|
||||
insert: 'O(n) 慢',
|
||||
delete: 'O(n) 慢',
|
||||
feature: '大小固定',
|
||||
applications: [
|
||||
{ icon: '📋', name: '列表数据', desc: '学生成绩、商品价格列表' },
|
||||
{ icon: '🖼️', name: '图像处理', desc: '像素矩阵存储' },
|
||||
{ icon: '📈', name: '统计图表', desc: '按时间顺序的数据' }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'linkedlist',
|
||||
name: '链表',
|
||||
icon: '🔗',
|
||||
tagline: '指针链接,灵活增删',
|
||||
access: 'O(n) 慢',
|
||||
insert: 'O(1) 快',
|
||||
delete: 'O(1) 快',
|
||||
feature: '大小可变',
|
||||
applications: [
|
||||
{ icon: '↩️', name: '撤销功能', desc: '操作历史记录' },
|
||||
{ icon: '🎵', name: '音乐播放', desc: '播放列表' },
|
||||
{ icon: '📝', name: '文本编辑', desc: '文档内容的动态存储' }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'stack',
|
||||
name: '栈',
|
||||
icon: '🥞',
|
||||
tagline: '后进先出',
|
||||
access: 'O(n)',
|
||||
insert: 'O(1) 快',
|
||||
delete: 'O(1) 快',
|
||||
feature: '一端操作',
|
||||
applications: [
|
||||
{ icon: '↩️', name: '撤销操作', desc: '编辑器的撤销功能' },
|
||||
{ icon: '🔙', name: '浏览器历史', desc: '后退按钮实现' },
|
||||
{ icon: '📞', name: '函数调用', desc: '程序调用栈管理' }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'queue',
|
||||
name: '队列',
|
||||
icon: '🚶',
|
||||
tagline: '先进先出',
|
||||
access: 'O(n)',
|
||||
insert: 'O(1) 快',
|
||||
delete: 'O(1) 快',
|
||||
feature: '两端操作',
|
||||
applications: [
|
||||
{ icon: '🖨️', name: '打印队列', desc: '文档按顺序打印' },
|
||||
{ icon: '🎫', name: '任务调度', desc: '操作系统进程调度' },
|
||||
{ icon: '💬', name: '消息队列', desc: '异步任务处理' }
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
const arrayData = ref([10, 25, 33, 47, 59, 62])
|
||||
const linkedListData = ref(['A', 'B', 'C', 'D', 'E'])
|
||||
const stackData = ref(['书5', '书4', '书3', '书2', '书1'])
|
||||
const queueData = ref(['人1', '人2', '人3', '人4'])
|
||||
|
||||
const currentStructure = computed(() => structures.find(s => s.id === activeStructure.value))
|
||||
|
||||
const pushStack = () => {
|
||||
const newItem = `书${stackData.value.length + 1}`
|
||||
stackData.value.unshift(newItem)
|
||||
}
|
||||
|
||||
const popStack = () => {
|
||||
if (stackData.value.length > 0) {
|
||||
stackData.value.shift()
|
||||
}
|
||||
}
|
||||
|
||||
const enqueue = () => {
|
||||
const newItem = `人${queueData.value.length + 1}`
|
||||
queueData.value.push(newItem)
|
||||
}
|
||||
|
||||
const dequeue = () => {
|
||||
if (queueData.value.length > 0) {
|
||||
queueData.value.shift()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.linear-structures-demo {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.demo-header .icon { font-size: 1.5rem; }
|
||||
.demo-header .title { font-weight: 700; font-size: 1.1rem; }
|
||||
.demo-header .subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
|
||||
|
||||
.structure-tabs {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 2rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.tab-btn {
|
||||
padding: 0.75rem 1.25rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
font-size: 0.9rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.tab-btn:hover {
|
||||
border-color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.tab-btn.active {
|
||||
background: var(--vp-c-brand);
|
||||
border-color: var(--vp-c-brand);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.structure-visual {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.visual-header {
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.structure-title {
|
||||
display: block;
|
||||
font-size: 1.3rem;
|
||||
font-weight: 700;
|
||||
color: var(--vp-c-brand);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.structure-tagline {
|
||||
font-size: 0.9rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.memory-block {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.array-cell {
|
||||
width: 70px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.cell-index {
|
||||
font-size: 0.75rem;
|
||||
color: var(--vp-c-text-2);
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.cell-value {
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--vp-c-brand-soft);
|
||||
border: 2px solid var(--vp-c-brand);
|
||||
border-radius: 6px;
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.nodes-chain {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.linked-node {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.node-data {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--vp-c-brand-soft);
|
||||
border: 2px solid var(--vp-c-brand);
|
||||
border-radius: 50%;
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.node-pointer {
|
||||
font-size: 1.5rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.linked-node.null .node-data {
|
||||
background: var(--vp-c-divider);
|
||||
border-color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.stack-container,
|
||||
.queue-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.stack-top,
|
||||
.queue-front {
|
||||
font-size: 0.9rem;
|
||||
color: var(--vp-c-brand);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.stack-items,
|
||||
.queue-items {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
min-height: 150px;
|
||||
}
|
||||
|
||||
.stack-item,
|
||||
.queue-item {
|
||||
width: 150px;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--vp-c-brand-soft);
|
||||
border: 2px solid var(--vp-c-brand);
|
||||
border-radius: 6px;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.queue-items {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.queue-item {
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.stack-bottom,
|
||||
.queue-rear {
|
||||
font-size: 0.85rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.stack-operations,
|
||||
.queue-operations {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: center;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.op-btn {
|
||||
padding: 0.6rem 1.25rem;
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.op-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.visual-note {
|
||||
text-align: center;
|
||||
font-size: 0.85rem;
|
||||
color: var(--vp-c-text-2);
|
||||
padding: 0.75rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.comparison-table {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.table-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
th {
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
padding: 0.75rem;
|
||||
text-align: center;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 0.75rem;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
tr.highlighted {
|
||||
background: var(--vp-c-brand-soft);
|
||||
}
|
||||
|
||||
.applications {
|
||||
border-top: 1px solid var(--vp-c-divider);
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
|
||||
.app-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.app-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.app-card {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.app-icon {
|
||||
font-size: 1.5rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.app-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.app-name {
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.app-desc {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
line-height: 1.4;
|
||||
}
|
||||
</style>
|
||||
+474
@@ -0,0 +1,474 @@
|
||||
<template>
|
||||
<div class="network-overview-demo">
|
||||
<div class="demo-header">
|
||||
<span class="icon">🌐</span>
|
||||
<span class="title">网络是怎么连接的</span>
|
||||
<span class="subtitle">从发送到接收的完整过程</span>
|
||||
</div>
|
||||
|
||||
<div class="network-scene">
|
||||
<div class="scene-devices">
|
||||
<!-- 发送方 -->
|
||||
<div class="device sender">
|
||||
<div class="device-icon">💻</div>
|
||||
<div class="device-name">发送方</div>
|
||||
<div class="device-ip">192.168.1.100</div>
|
||||
<div class="app-layer">
|
||||
<div class="app-icon">📧</div>
|
||||
<div class="app-name">邮件应用</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 网络路径 -->
|
||||
<div class="network-path">
|
||||
<div class="path-steps">
|
||||
<div
|
||||
v-for="(step, index) in pathSteps"
|
||||
:key="index"
|
||||
:class="['path-step', { active: activeStep === index }]"
|
||||
@click="activeStep = index"
|
||||
>
|
||||
<div class="step-icon">{{ step.icon }}</div>
|
||||
<div class="step-name">{{ step.name }}</div>
|
||||
<div class="step-desc">{{ step.desc }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="data-flow">
|
||||
<div v-if="activeStep !== null" class="flow-animation">
|
||||
<div class="flow-packet">📦 数据包</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 接收方 -->
|
||||
<div class="device receiver">
|
||||
<div class="device-icon">🖥️</div>
|
||||
<div class="device-name">接收方</div>
|
||||
<div class="device-ip">192.168.1.200</div>
|
||||
<div class="app-layer">
|
||||
<div class="app-icon">📧</div>
|
||||
<div class="app-name">邮件应用</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 封装过程 -->
|
||||
<div class="encapsulation-process">
|
||||
<div class="process-title">数据封装过程</div>
|
||||
<div class="encapsulation-layers">
|
||||
<div
|
||||
v-for="(layer, index) in encapsulationLayers"
|
||||
:key="index"
|
||||
:class="['encap-layer', { active: activeStep === index }]"
|
||||
>
|
||||
<div class="layer-header">
|
||||
<span class="layer-num">{{ layer.num }}</span>
|
||||
<span class="layer-name">{{ layer.name }}</span>
|
||||
</div>
|
||||
<div class="layer-content">
|
||||
<div class="layer-data">{{ layer.data }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 协议栈 -->
|
||||
<div class="protocol-stack">
|
||||
<div class="stack-title">网络协议栈 (OSI 模型)</div>
|
||||
<div class="stack-container">
|
||||
<div class="stack-column sender-stack">
|
||||
<div class="stack-header">发送方</div>
|
||||
<div
|
||||
v-for="(layer, index) in protocolLayers"
|
||||
:key="'sender-' + index"
|
||||
:class="['stack-layer', { highlighted: activeStep === index }]"
|
||||
>
|
||||
{{ layer }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stack-arrow">→</div>
|
||||
|
||||
<div class="stack-column receiver-stack">
|
||||
<div class="stack-header">接收方</div>
|
||||
<div
|
||||
v-for="(layer, index) in protocolLayers"
|
||||
:key="'receiver-' + index"
|
||||
:class="['stack-layer', { highlighted: activeStep === index }]"
|
||||
>
|
||||
{{ layer }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const activeStep = ref(null)
|
||||
|
||||
const pathSteps = [
|
||||
{
|
||||
icon: '📧',
|
||||
name: '应用层',
|
||||
desc: '邮件软件创建邮件内容'
|
||||
},
|
||||
{
|
||||
icon: '🔐',
|
||||
name: '传输层',
|
||||
desc: 'TCP 添加端口号和序号'
|
||||
},
|
||||
{
|
||||
icon: '🌐',
|
||||
name: '网络层',
|
||||
desc: 'IP 添加源地址和目标地址'
|
||||
},
|
||||
{
|
||||
icon: '🔌',
|
||||
name: '数据链路层',
|
||||
desc: '以太网添加 MAC 地址'
|
||||
},
|
||||
{
|
||||
icon: '⚡',
|
||||
name: '物理层',
|
||||
desc: '转换成电信号发送'
|
||||
}
|
||||
]
|
||||
|
||||
const encapsulationLayers = [
|
||||
{
|
||||
num: '7',
|
||||
name: '应用层',
|
||||
data: '邮件内容: "Hello!"'
|
||||
},
|
||||
{
|
||||
num: '6',
|
||||
name: '表示层',
|
||||
data: '数据编码: UTF-8'
|
||||
},
|
||||
{
|
||||
num: '5',
|
||||
name: '会话层',
|
||||
data: '会话ID: sess_123'
|
||||
},
|
||||
{
|
||||
num: '4',
|
||||
name: '传输层',
|
||||
data: 'TCP 头: 端口 25'
|
||||
},
|
||||
{
|
||||
num: '3',
|
||||
name: '网络层',
|
||||
data: 'IP 头: 192.168.1.100 → 192.168.1.200'
|
||||
},
|
||||
{
|
||||
num: '2',
|
||||
name: '数据链路层',
|
||||
data: '以太网帧: MAC 地址'
|
||||
},
|
||||
{
|
||||
num: '1',
|
||||
name: '物理层',
|
||||
data: '比特流: 01010101...'
|
||||
}
|
||||
]
|
||||
|
||||
const protocolLayers = [
|
||||
'应用层 (HTTP, SMTP)',
|
||||
'传输层 (TCP, UDP)',
|
||||
'网络层 (IP)',
|
||||
'数据链路层 (Ethernet)',
|
||||
'物理层 (电信号)'
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.network-overview-demo {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.demo-header .icon { font-size: 1.5rem; }
|
||||
.demo-header .title { font-weight: 700; font-size: 1.1rem; }
|
||||
.demo-header .subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
|
||||
|
||||
.network-scene {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.scene-devices {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.device {
|
||||
flex: 1;
|
||||
max-width: 200px;
|
||||
text-align: center;
|
||||
padding: 1.5rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.device-icon {
|
||||
font-size: 3rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.device-name {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.device-ip {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.app-layer {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.75rem;
|
||||
background: var(--vp-c-brand-soft);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.app-icon { font-size: 1.5rem; }
|
||||
.app-name { font-size: 0.8rem; font-weight: 600; color: var(--vp-c-brand); }
|
||||
|
||||
.network-path {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.path-steps {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.path-step {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 0.75rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.path-step:hover {
|
||||
border-color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.path-step.active {
|
||||
border-color: var(--vp-c-brand);
|
||||
background: var(--vp-c-brand-soft);
|
||||
}
|
||||
|
||||
.step-icon {
|
||||
font-size: 1.5rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.step-name {
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.step-desc {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.data-flow {
|
||||
text-align: center;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.flow-animation {
|
||||
animation: flowMove 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes flowMove {
|
||||
0%, 100% { transform: translateX(-20px); opacity: 0; }
|
||||
50% { transform: translateX(20px); opacity: 1; }
|
||||
}
|
||||
|
||||
.flow-packet {
|
||||
display: inline-block;
|
||||
padding: 0.5rem 1rem;
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
border-radius: 20px;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.encapsulation-process {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.process-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.encapsulation-layers {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.encap-layer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 0.75rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.encap-layer.active {
|
||||
border-color: var(--vp-c-brand);
|
||||
background: var(--vp-c-brand-soft);
|
||||
}
|
||||
|
||||
.layer-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
min-width: 150px;
|
||||
}
|
||||
|
||||
.layer-num {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.layer-name {
|
||||
font-weight: 600;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.layer-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.layer-data {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.protocol-stack {
|
||||
border-top: 1px solid var(--vp-c-divider);
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
|
||||
.stack-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.stack-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.stack-column {
|
||||
flex: 1;
|
||||
max-width: 250px;
|
||||
}
|
||||
|
||||
.stack-header {
|
||||
text-align: center;
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0.75rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.stack-layer {
|
||||
padding: 0.6rem;
|
||||
margin-bottom: 0.5rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 4px;
|
||||
font-size: 0.8rem;
|
||||
text-align: center;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.stack-layer.highlighted {
|
||||
border-color: var(--vp-c-brand);
|
||||
background: var(--vp-c-brand-soft);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.stack-arrow {
|
||||
font-size: 2rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
@media (max-width: 968px) {
|
||||
.scene-devices {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.network-path {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.stack-container {
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.stack-arrow {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
+236
@@ -0,0 +1,236 @@
|
||||
<template>
|
||||
<div class="network-principle-demo">
|
||||
<div class="demo-header">
|
||||
<span class="icon">🌐</span>
|
||||
<span class="title">网络基本原理</span>
|
||||
<span class="subtitle">数据如何在网络中传输</span>
|
||||
</div>
|
||||
|
||||
<div class="principle-cards">
|
||||
<div
|
||||
v-for="(principle, index) in principles"
|
||||
:key="index"
|
||||
class="principle-card"
|
||||
>
|
||||
<div class="card-icon">{{ principle.icon }}</div>
|
||||
<div class="card-title">{{ principle.title }}</div>
|
||||
<div class="card-desc">{{ principle.desc }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="data-flow">
|
||||
<div class="flow-title">数据传输流程</div>
|
||||
<div class="flow-diagram">
|
||||
<div class="flow-step">
|
||||
<div class="step-icon">📤</div>
|
||||
<div class="step-text">发送方</div>
|
||||
</div>
|
||||
<div class="flow-arrow">→</div>
|
||||
<div class="flow-step">
|
||||
<div class="step-icon">📦</div>
|
||||
<div class="step-text">封装数据包</div>
|
||||
</div>
|
||||
<div class="flow-arrow">→</div>
|
||||
<div class="flow-step">
|
||||
<div class="step-icon">🌐</div>
|
||||
<div class="step-text">网络传输</div>
|
||||
</div>
|
||||
<div class="flow-arrow">→</div>
|
||||
<div class="flow-step">
|
||||
<div class="step-icon">📥</div>
|
||||
<div class="step-text">接收方</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="key-concepts">
|
||||
<div class="concepts-title">核心概念</div>
|
||||
<div class="concepts-list">
|
||||
<div class="concept-item">
|
||||
<div class="concept-name">IP 地址</div>
|
||||
<div class="concept-value">192.168.1.100</div>
|
||||
<div class="concept-desc">设备的网络地址</div>
|
||||
</div>
|
||||
<div class="concept-item">
|
||||
<div class="concept-name">端口</div>
|
||||
<div class="concept-value">80, 443, 22</div>
|
||||
<div class="concept-desc">应用程序的标识</div>
|
||||
</div>
|
||||
<div class="concept-item">
|
||||
<div class="concept-name">协议</div>
|
||||
<div class="concept-value">HTTP, TCP/IP</div>
|
||||
<div class="concept-desc">通信的规则</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const principles = [
|
||||
{
|
||||
icon: '📡',
|
||||
title: '分组交换',
|
||||
desc: '数据被分成小块独立传输,然后重组'
|
||||
},
|
||||
{
|
||||
icon: '🔄',
|
||||
title: '路由转发',
|
||||
desc: '路由器根据地址决定数据包的转发路径'
|
||||
},
|
||||
{
|
||||
icon: '📋',
|
||||
title: '协议分层',
|
||||
desc: '不同层次负责不同的通信功能'
|
||||
},
|
||||
{
|
||||
icon: '🔐',
|
||||
title: '可靠传输',
|
||||
desc: 'TCP 确保数据完整、有序地到达目的地'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.network-principle-demo {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.demo-header .icon { font-size: 1.5rem; }
|
||||
.demo-header .title { font-weight: 700; font-size: 1.1rem; }
|
||||
.demo-header .subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
|
||||
|
||||
.principle-cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 1rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.principle-card {
|
||||
padding: 1.25rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.card-icon {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.card-desc {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.data-flow {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.flow-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
text-align: center;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.flow-diagram {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.flow-step {
|
||||
text-align: center;
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.step-icon {
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.step-text {
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.flow-arrow {
|
||||
font-size: 1.5rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.key-concepts {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.concepts-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.concepts-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.concept-item {
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 6px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.concept-name {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.concept-value {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 0.9rem;
|
||||
color: var(--vp-c-brand);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.concept-desc {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
</style>
|
||||
+324
@@ -0,0 +1,324 @@
|
||||
<template>
|
||||
<div class="os-overview-demo">
|
||||
<div class="demo-header">
|
||||
<span class="icon">🎛️</span>
|
||||
<span class="title">操作系统:计算机的"大管家"</span>
|
||||
<span class="subtitle">让多个程序和谐共处的艺术</span>
|
||||
</div>
|
||||
|
||||
<div class="demo-content">
|
||||
<div class="os-layers">
|
||||
<div class="layer user-apps">
|
||||
<div class="layer-title">
|
||||
应用程序层
|
||||
</div>
|
||||
<div class="layer-content">
|
||||
<div
|
||||
v-for="app in applications"
|
||||
:key="app.id"
|
||||
class="app-icon"
|
||||
:class="{ active: activeApp === app.id }"
|
||||
@click="activeApp = app.id"
|
||||
:title="app.name"
|
||||
>
|
||||
{{ app.icon }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="layer-desc">
|
||||
用户直接使用的程序(浏览器、IDE、游戏等)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layer kernel">
|
||||
<div class="layer-title">
|
||||
操作系统内核
|
||||
</div>
|
||||
<div class="layer-content">
|
||||
<div class="kernel-components">
|
||||
<div
|
||||
v-for="component in kernelComponents"
|
||||
:key="component.id"
|
||||
class="kernel-component"
|
||||
:class="{ active: activeComponent === component.id }"
|
||||
@click="activeComponent = component.id"
|
||||
>
|
||||
{{ component.icon }} {{ component.name }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layer-desc">
|
||||
进程管理、内存管理、文件系统、设备管理
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layer hardware">
|
||||
<div class="layer-title">
|
||||
硬件层
|
||||
</div>
|
||||
<div class="layer-content">
|
||||
<div class="hardware-icons">
|
||||
<span>💻 CPU</span>
|
||||
<span>🧠 RAM</span>
|
||||
<span>💾 硬盘</span>
|
||||
<span>🖥️ GPU</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="resource-flow">
|
||||
<div class="flow-title">资源流向</div>
|
||||
<div class="flow-content">
|
||||
<div class="flow-item" :class="{ active: showFlow }">
|
||||
<div class="flow-arrow">↓</div>
|
||||
<div class="flow-desc">
|
||||
应用程序请求资源(内存、CPU、文件)
|
||||
</div>
|
||||
</div>
|
||||
<div class="flow-item" :class="{ active: showFlow }">
|
||||
<div class="flow-arrow">↓</div>
|
||||
<div class="flow-desc">
|
||||
操作系统内核统一分配和调度
|
||||
</div>
|
||||
</div>
|
||||
<div class="flow-item" :class="{ active: showFlow }">
|
||||
<div class="flow-arrow">↓</div>
|
||||
<div class="flow-desc">
|
||||
硬件执行实际操作
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
class="flow-btn"
|
||||
@click="showFlow = !showFlow"
|
||||
>
|
||||
{{ showFlow ? '隐藏' : '显示' }}资源流
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="demo-details">
|
||||
<div class="detail-item">
|
||||
<div class="detail-title">
|
||||
当前选中:{{ activeAppName || '请选择应用程序' }}
|
||||
</div>
|
||||
<div class="detail-desc">
|
||||
{{ getActiveAppDesc() }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const activeApp = ref('browser')
|
||||
const activeComponent = ref('process')
|
||||
const showFlow = ref(false)
|
||||
|
||||
const applications = [
|
||||
{ id: 'browser', name: '浏览器', icon: '🌐' },
|
||||
{ id: 'ide', name: '代码编辑器', icon: '💻' },
|
||||
{ id: 'music', name: '音乐播放器', icon: '🎵' },
|
||||
{ id: 'video', name: '视频播放器', icon: '🎬' },
|
||||
{ id: 'game', name: '游戏', icon: '🎮' }
|
||||
]
|
||||
|
||||
const kernelComponents = [
|
||||
{ id: 'process', name: '进程管理', icon: '🔄' },
|
||||
{ id: 'memory', name: '内存管理', icon: '🧠' },
|
||||
{ id: 'filesystem', name: '文件系统', icon: '📁' },
|
||||
{ id: 'device', name: '设备管理', icon: '🔧' }
|
||||
]
|
||||
|
||||
const activeAppName = computed(() => {
|
||||
const app = applications.find(a => a.id === activeApp.value)
|
||||
return app?.name || ''
|
||||
})
|
||||
|
||||
const getActiveAppDesc = () => {
|
||||
const component = kernelComponents.find(c => c.id === activeComponent.value)
|
||||
const app = applications.find(a => a.id === activeApp.value)
|
||||
|
||||
if (!app || !component) return '请选择应用程序和内核组件'
|
||||
return `${app.name} 需要使用 ${component.name} 的功能`
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.os-overview-demo {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.demo-header .icon { font-size: 1.5rem; }
|
||||
.demo-header .title { font-weight: 700; font-size: 1.1rem; }
|
||||
.demo-header .subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
|
||||
|
||||
.os-layers {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.layer {
|
||||
padding: 1rem;
|
||||
border-radius: 8px;
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.layer.user-apps {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.layer.kernel {
|
||||
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.layer.hardware {
|
||||
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.layer-title {
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.75rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.layer-content {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.app-icon {
|
||||
font-size: 1.8rem;
|
||||
padding: 0.5rem;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.app-icon:hover, .app-icon.active {
|
||||
transform: scale(1.1);
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.kernel-component {
|
||||
padding: 0.4rem 0.6rem;
|
||||
border-radius: 4px;
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
cursor: pointer;
|
||||
font-size: 0.85rem;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.kernel-component:hover, .kernel-component.active {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.hardware-icons {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
font-size: 1.2rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.layer-desc {
|
||||
font-size: 0.85rem;
|
||||
margin-top: 0.5rem;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.resource-flow {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.flow-title {
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.75rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.flow-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.flow-item {
|
||||
text-align: center;
|
||||
font-size: 0.85rem;
|
||||
padding: 0.5rem;
|
||||
opacity: 0.6;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.flow-item.active {
|
||||
opacity: 1;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.flow-arrow {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.flow-btn {
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
margin-top: 0.75rem;
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 0.85rem;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.flow-btn:hover {
|
||||
background: var(--vp-c-brand-dark);
|
||||
}
|
||||
|
||||
.demo-details {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.detail-title {
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.5rem;
|
||||
font-size: 0.9rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.detail-desc {
|
||||
font-size: 0.85rem;
|
||||
color: var(--vP-c-text-2);
|
||||
line-height: 1.5;
|
||||
}
|
||||
</style>
|
||||
+397
@@ -0,0 +1,397 @@
|
||||
<template>
|
||||
<div class="physical-layer-demo">
|
||||
<div class="demo-header">
|
||||
<span class="icon">⚡</span>
|
||||
<span class="title">物理层:电信号的传递</span>
|
||||
<span class="subtitle">比特如何通过物理介质传输</span>
|
||||
</div>
|
||||
|
||||
<div class="media-selector">
|
||||
<div class="selector-label">选择传输介质:</div>
|
||||
<div class="media-buttons">
|
||||
<button
|
||||
v-for="media in mediaTypes"
|
||||
:key="media.id"
|
||||
:class="['media-btn', { active: activeMedia === media.id }]"
|
||||
@click="activeMedia = media.id"
|
||||
>
|
||||
{{ media.icon }} {{ media.name }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 信号可视化 -->
|
||||
<div class="signal-visualization">
|
||||
<div class="signal-header">
|
||||
<span class="signal-title">{{ currentMedia.signalName }}</span>
|
||||
<span class="signal-desc">{{ currentMedia.signalDesc }}</span>
|
||||
</div>
|
||||
|
||||
<div class="signal-canvas">
|
||||
<div class="signal-wave">
|
||||
<svg viewBox="0 0 800 150" class="wave-svg">
|
||||
<!-- 坐标轴 -->
|
||||
<line x1="50" y1="75" x2="750" y2="75" stroke="var(--vp-c-divider)" stroke-width="2" />
|
||||
|
||||
<!-- 信号波形 -->
|
||||
<path
|
||||
:d="currentMedia.wavePath"
|
||||
fill="none"
|
||||
:stroke="activeMedia === 'fiber' ? '#ff6b6b' : 'var(--vp-c-brand)'"
|
||||
stroke-width="3"
|
||||
class="signal-path"
|
||||
/>
|
||||
|
||||
<!-- 数据标记 -->
|
||||
<g v-if="activeMedia === 'copper'">
|
||||
<text x="100" y="40" fill="var(--vp-c-text-2)" font-size="12">1</text>
|
||||
<text x="180" y="110" fill="var(--vp-c-text-2)" font-size="12">0</text>
|
||||
<text x="260" y="40" fill="var(--vp-c-text-2)" font-size="12">1</text>
|
||||
<text x="340" y="40" fill="var(--vp-c-text-2)" font-size="12">1</text>
|
||||
<text x="420" y="110" fill="var(--vp-c-text-2)" font-size="12">0</text>
|
||||
</g>
|
||||
|
||||
<g v-if="activeMedia === 'fiber'">
|
||||
<text x="100" y="40" fill="#ff6b6b" font-size="12">开</text>
|
||||
<text x="180" y="110" fill="var(--vp-c-text-2)" font-size="12">关</text>
|
||||
<text x="260" y="40" fill="#ff6b6b" font-size="12">开</text>
|
||||
<text x="340" y="40" fill="#ff6b6b" font-size="12">开</text>
|
||||
<text x="420" y="110" fill="var(--vp-c-text-2)" font-size="12">关</text>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div class="signal-legend">
|
||||
<div class="legend-item">
|
||||
<div class="legend-color high"></div>
|
||||
<span class="legend-label">高电平/开 (1)</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<div class="legend-color low"></div>
|
||||
<span class="legend-label">低电平/关 (0)</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 介质特性 -->
|
||||
<div class="media-specs">
|
||||
<div class="specs-grid">
|
||||
<div class="spec-card">
|
||||
<div class="spec-icon">🚀</div>
|
||||
<div class="spec-content">
|
||||
<div class="spec-label">传输速度</div>
|
||||
<div class="spec-value">{{ currentMedia.speed }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="spec-card">
|
||||
<div class="spec-icon">📏</div>
|
||||
<div class="spec-content">
|
||||
<div class="spec-label">最大距离</div>
|
||||
<div class="spec-value">{{ currentMedia.distance }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="spec-card">
|
||||
<div class="spec-icon">🛡️</div>
|
||||
<div class="spec-content">
|
||||
<div class="spec-label">抗干扰能力</div>
|
||||
<div class="spec-value">{{ currentMedia.immunity }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="spec-card">
|
||||
<div class="spec-icon">💰</div>
|
||||
<div class="spec-content">
|
||||
<div class="spec-label">成本</div>
|
||||
<div class="spec-value">{{ currentMedia.cost }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 应用场景 -->
|
||||
<div class="applications">
|
||||
<div class="app-title">典型应用场景</div>
|
||||
<div class="app-list">
|
||||
<div v-for="(app, index) in currentMedia.applications" :key="index" class="app-item">
|
||||
<span class="app-icon">{{ app.icon }}</span>
|
||||
<span class="app-text">{{ app.text }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const activeMedia = ref('copper')
|
||||
|
||||
const mediaTypes = [
|
||||
{ id: 'copper', name: '双绞线', icon: '🔌' },
|
||||
{ id: 'fiber', name: '光纤', icon: '💡' },
|
||||
{ id: 'wireless', name: '无线', icon: '📡' }
|
||||
]
|
||||
|
||||
const mediaData = {
|
||||
copper: {
|
||||
signalName: '电信号(电压高低)',
|
||||
signalDesc: '用高低电压表示 0 和 1',
|
||||
wavePath: 'M 50 75 L 100 75 L 100 25 L 150 25 L 150 125 L 200 125 L 200 25 L 250 25 L 250 25 L 300 25 L 300 125 L 350 125 L 350 25 L 400 25',
|
||||
speed: '最高 10 Gbps',
|
||||
distance: '100 米',
|
||||
immunity: '较差(易受电磁干扰)',
|
||||
cost: '低',
|
||||
applications: [
|
||||
{ icon: '🏠', text: '家庭局域网(网线连接)' },
|
||||
{ icon: '🏢', text: '办公室网络布线' },
|
||||
{ icon: '🖥️', text: '电脑连接路由器' }
|
||||
]
|
||||
},
|
||||
fiber: {
|
||||
signalName: '光信号(光的开关)',
|
||||
signalDesc: '用光脉冲表示 0 和 1',
|
||||
wavePath: 'M 50 75 L 100 75 L 100 25 L 150 25 L 150 125 L 200 125 L 200 25 L 250 25 L 250 25 L 300 25 L 300 125 L 350 125 L 350 25 L 400 25',
|
||||
speed: '最高 100+ Tbps',
|
||||
distance: '几十公里',
|
||||
immunity: '极强(不受电磁干扰)',
|
||||
cost: '高',
|
||||
applications: [
|
||||
{ icon: '🌐', text: '互联网骨干网' },
|
||||
{ icon: '🏢', text: '跨楼宇网络连接' },
|
||||
{ icon: '📺', text: '光纤入户(FTTH)' }
|
||||
]
|
||||
},
|
||||
wireless: {
|
||||
signalName: '电磁波(无线电波)',
|
||||
signalDesc: '用不同频率的电磁波表示数据',
|
||||
wavePath: 'M 50 75 Q 87.5 25 125 75 T 200 75 T 275 75 T 350 75 T 425 75',
|
||||
speed: '最高 10+ Gbps (WiFi 6E)',
|
||||
distance: '几十米到几公里',
|
||||
immunity: '一般(易受障碍物影响)',
|
||||
cost: '中等',
|
||||
applications: [
|
||||
{ icon: '📱', text: '手机连接移动网络' },
|
||||
{ icon: '💻', text: '笔记本 WiFi 上网' },
|
||||
{ icon: '🎮', text: '蓝牙设备连接' }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
const currentMedia = computed(() => mediaData[activeMedia.value])
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.physical-layer-demo {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.demo-header .icon { font-size: 1.5rem; }
|
||||
.demo-header .title { font-weight: 700; font-size: 1.1rem; }
|
||||
.demo-header .subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
|
||||
|
||||
.media-selector {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.selector-label {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.media-buttons {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.media-btn {
|
||||
padding: 0.6rem 1rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 0.9rem;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.media-btn:hover {
|
||||
border-color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.media-btn.active {
|
||||
background: var(--vp-c-brand);
|
||||
border-color: var(--vp-c-brand);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.signal-visualization {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.25rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.signal-header {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.signal-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
display: block;
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.signal-desc {
|
||||
font-size: 0.85rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.signal-canvas {
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 6px;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.signal-wave {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.wave-svg {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.signal-path {
|
||||
animation: drawSignal 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes drawSignal {
|
||||
0% { stroke-dashoffset: 1000; }
|
||||
100% { stroke-dashoffset: 0; }
|
||||
}
|
||||
|
||||
.signal-legend {
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.legend-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.legend-color {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.legend-color.high {
|
||||
background: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.legend-color.low {
|
||||
background: var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.media-specs {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.specs-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.spec-card {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.spec-icon {
|
||||
font-size: 1.5rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.spec-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.spec-label {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.spec-value {
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.applications {
|
||||
border-top: 1px solid var(--vp-c-divider);
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
|
||||
.app-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.app-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.app-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 0.75rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.app-icon {
|
||||
font-size: 1.3rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.app-text {
|
||||
font-size: 0.85rem;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
</style>
|
||||
+429
@@ -0,0 +1,429 @@
|
||||
<template>
|
||||
<div class="pmf-collab-demo">
|
||||
<div class="demo-header">
|
||||
<span class="icon">🤝</span>
|
||||
<span class="title">进程、内存、文件系统的协作</span>
|
||||
<span class="subtitle">三大管理模块如何协同工作</span>
|
||||
</div>
|
||||
|
||||
<div class="demo-content">
|
||||
<div class="collab-scene">
|
||||
<div class="scene-title">
|
||||
场景选择:
|
||||
</div>
|
||||
<div class="scene-buttons">
|
||||
<button
|
||||
v-for="scene in scenes"
|
||||
:key="scene.id"
|
||||
:class="['scene-btn', { active: activeScene === scene.id }]"
|
||||
@click="activeScene = scene.id"
|
||||
>
|
||||
{{ scene.icon }} {{ scene.name }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="collab-visualization">
|
||||
<div class="vis-container">
|
||||
<!-- 进程区域 -->
|
||||
<div class="zone process-zone">
|
||||
<div class="zone-header">
|
||||
<span class="zone-icon">🔄</span>
|
||||
<span class="zone-name">进程管理</span>
|
||||
</div>
|
||||
<div class="zone-content">
|
||||
<div class="process-list">
|
||||
<div
|
||||
v-for="proc in processes"
|
||||
:key="proc.id"
|
||||
class="process-item"
|
||||
:class="{ active: proc.id === currentProcessId }"
|
||||
>
|
||||
<span class="proc-name">{{ proc.name }}</span>
|
||||
<span class="proc-pid">PID: {{ proc.pid }}</span>
|
||||
<span class="proc-state">{{ proc.state }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 内存区域 -->
|
||||
<div class="zone memory-zone">
|
||||
<div class="zone-header">
|
||||
<span class="zone-icon">🧠</span>
|
||||
<span class="zone-name">内存管理</span>
|
||||
</div>
|
||||
<div class="zone-content">
|
||||
<div class="memory-grid">
|
||||
<div
|
||||
v-for="(block, index) in memoryBlocks"
|
||||
:key="index"
|
||||
class="memory-block"
|
||||
:class="{
|
||||
allocated: block.allocated,
|
||||
process: block.processId
|
||||
}"
|
||||
:title="`地址: ${block.address}, 大小: ${block.size}KB`"
|
||||
>
|
||||
<div v-if="block.allocated" class="block-info">
|
||||
{{ getProcessName(block.processId) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 文件系统区域 -->
|
||||
<div class="zone filesystem-zone">
|
||||
<div class="zone-header">
|
||||
<span class="zone-icon">📁</span>
|
||||
<span class="zone-name">文件系统</span>
|
||||
</div>
|
||||
<div class="zone-content">
|
||||
<div class="file-tree">
|
||||
<div
|
||||
v-for="file in files"
|
||||
:key="file.id"
|
||||
class="file-item"
|
||||
:class="{ active: file.id === currentFileId }"
|
||||
>
|
||||
<span class="file-icon">{{ getIcon(file.type) }}</span>
|
||||
<span class="file-name">{{ file.name }}</span>
|
||||
<span v-if="file.size" class="file-size">{{ file.size }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="explanation">
|
||||
<div class="exp-title">
|
||||
{{ currentSceneData.title }}
|
||||
</div>
|
||||
<div class="exp-content">
|
||||
<div
|
||||
v-for="(step, index) in currentSceneData.steps"
|
||||
:key="index"
|
||||
class="exp-step"
|
||||
>
|
||||
<span class="step-number">{{ index + 1 }}</span>
|
||||
<span class="step-text">{{ step }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const activeScene = ref('launch')
|
||||
const currentProcessId = ref(null)
|
||||
const currentFileId = ref(null)
|
||||
|
||||
const scenes = [
|
||||
{
|
||||
id: 'launch',
|
||||
name: '启动程序',
|
||||
icon: '🚀'
|
||||
},
|
||||
{
|
||||
id: 'memory-access',
|
||||
name: '内存访问',
|
||||
icon: '💾'
|
||||
},
|
||||
{
|
||||
id: 'file-access',
|
||||
name: '文件读写',
|
||||
icon: '📄'
|
||||
},
|
||||
{
|
||||
id: 'context-switch',
|
||||
name: '进程切换',
|
||||
icon: '🔄'
|
||||
}
|
||||
]
|
||||
|
||||
const processes = ref([
|
||||
{ id: 1, name: '浏览器', pid: 1001, state: '运行中' },
|
||||
{ id: 2, name: '音乐播放器', pid: 1002, state: '等待中' },
|
||||
{ id: 3, name: '代码编辑器', pid: 1003, state: '运行中' }
|
||||
])
|
||||
|
||||
const memoryBlocks = ref([
|
||||
{ address: '0x1000', size: 256, allocated: true, processId: 1 },
|
||||
{ address: '0x2000', size: 128, allocated: true, processId: 2 },
|
||||
{ address: '0x3000', size: 512, allocated: true, processId: 3 },
|
||||
{ address: '0x4000', size: 1024, allocated: false, processId: null },
|
||||
{ address: '0x5000', size: 512, allocated: false, processId: null },
|
||||
{ address: '0x6000', size: 256, allocated: false, processId: null },
|
||||
{ address: '0x7000', size: 128, allocated: false, processId: null }
|
||||
])
|
||||
|
||||
const files = ref([
|
||||
{ id: 1, name: 'config.json', type: 'json', size: '2KB' },
|
||||
{ id: 2, name: 'user_data.db', type: 'db', size: '50MB' },
|
||||
{ id: 3, name: 'cache', type: 'folder', size: '' },
|
||||
{ id: 4, name: 'song.mp3', type: 'audio', size: '5MB' }
|
||||
])
|
||||
|
||||
const sceneData = {
|
||||
launch: {
|
||||
title: '场景1:启动程序(浏览器)',
|
||||
steps: [
|
||||
'1. 用户双击浏览器图标',
|
||||
'2. 进程管理创建新进程(PID: 1004)',
|
||||
'3. 内存管理分配内存空间(代码段、数据段、堆、栈)',
|
||||
'4. 文件系统读取配置文件和缓存数据'
|
||||
]
|
||||
},
|
||||
'memory-access': {
|
||||
title: '场景2:程序运行时申请内存',
|
||||
steps: [
|
||||
'1. 浏览器加载大图片,需要更多内存',
|
||||
'2. 进程通过系统调用请求内存(malloc)',
|
||||
'3. 内存管理查找可用内存块(如:0x4000)',
|
||||
'4. 将内存块标记为"已分配",返回地址给程序'
|
||||
]
|
||||
},
|
||||
'file-access': {
|
||||
title: '场景3:保存文件',
|
||||
steps: [
|
||||
'1. 用户在浏览器点击"保存图片"',
|
||||
'2. 进程发起文件写入系统调用',
|
||||
'3. 文件系统查找空闲磁盘空间',
|
||||
'4. 将数据写入磁盘,更新文件分配表'
|
||||
]
|
||||
},
|
||||
'context-switch': {
|
||||
title: '场景4:切换到音乐播放器',
|
||||
steps: [
|
||||
'1. 用户点击音乐播放器窗口',
|
||||
'2. 操作系统暂停浏览器进程',
|
||||
'3. 调度器加载音乐播放器进程上下文',
|
||||
'4. CPU开始执行音乐播放器代码'
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
const currentSceneData = computed(() => sceneData[activeScene.value] || sceneData.launch)
|
||||
|
||||
const getProcessName = (id) => {
|
||||
const proc = processes.value.find(p => p.id === id)
|
||||
return proc?.name || '系统'
|
||||
}
|
||||
|
||||
const getIcon = (type) => {
|
||||
const icons = {
|
||||
'json': '📋',
|
||||
'db': '🗄️',
|
||||
'folder': '📁',
|
||||
'audio': '🎵'
|
||||
}
|
||||
return icons[type] || '📄'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.pmf-collab-demo {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.demo-header .icon { font-size: 1.5rem; }
|
||||
.demo-header .title { font-weight: 700; font-size: 1.1rem; }
|
||||
.demo-header .subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
|
||||
|
||||
.scene-buttons {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.scene-btn {
|
||||
padding: 0.6rem 1rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 0.9rem;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.scene-btn:hover {
|
||||
border-color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.scene-btn.active {
|
||||
background: var(--vp-c-brand);
|
||||
border-color: var(--vp-c-brand);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.collab-visualization {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.collab-visualization {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.zone {
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg);
|
||||
}
|
||||
|
||||
.zone-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 0.75rem;
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.zone-icon { font-size: 1.2rem; }
|
||||
.zone-name { font-size: 0.85rem; }
|
||||
|
||||
.process-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.process-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0.5rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 4px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.process-item.active {
|
||||
border: 2px solid var(--vp-c-brand);
|
||||
background: var(--vp-c-brand-soft);
|
||||
}
|
||||
|
||||
.proc-name, .proc-pid, .proc-state {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.proc-state {
|
||||
color: var(--vp-c-text-2);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.memory-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.memory-block {
|
||||
aspect-ratio: 2;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.75rem;
|
||||
position: relative;
|
||||
background: var(--vp-c-bg-soft);
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.memory-block.allocated {
|
||||
background: var(--vp-c-brand-soft);
|
||||
border-color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.memory-block.process {
|
||||
border: 2px solid var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.block-info {
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-brand);
|
||||
font-size: 0.7rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.file-tree {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.file-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 4px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.file-item.active {
|
||||
border: 2px solid var(--vp-c-brand);
|
||||
background: var(--vp-c-brand-soft);
|
||||
}
|
||||
|
||||
.file-name { flex: 1; }
|
||||
.file-size { color: var(--vp-c-text-2); font-size: 0.7rem; }
|
||||
|
||||
.explanation {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.exp-title {
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.75rem;
|
||||
font-size: 0.95rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.exp-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.exp-step {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.step-number {
|
||||
color: var(--vp-c-brand);
|
||||
font-weight: 600;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
</style>
|
||||
+441
@@ -0,0 +1,441 @@
|
||||
<template>
|
||||
<div class="programming-language-comparison-demo">
|
||||
<div class="demo-header">
|
||||
<span class="icon">⚖️</span>
|
||||
<span class="title">编程语言对比</span>
|
||||
<span class="subtitle">不同语言的特点和适用场景</span>
|
||||
</div>
|
||||
|
||||
<div class="comparison-grid">
|
||||
<div
|
||||
v-for="lang in languages"
|
||||
:key="lang.name"
|
||||
:class="['lang-card', { active: activeLang === lang.name }]"
|
||||
@click="activeLang = lang.name"
|
||||
>
|
||||
<div class="card-header">
|
||||
<span class="card-icon">{{ lang.icon }}</span>
|
||||
<span class="card-name">{{ lang.name }}</span>
|
||||
</div>
|
||||
<div class="card-year">{{ lang.year }}</div>
|
||||
<div class="card-paradigm">{{ lang.paradigm }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 详细对比 -->
|
||||
<div v-if="activeLang" class="detail-comparison">
|
||||
<div class="detail-header">
|
||||
<span class="detail-icon">{{ currentLang.icon }}</span>
|
||||
<span class="detail-name">{{ currentLang.name }}</span>
|
||||
</div>
|
||||
|
||||
<div class="detail-content">
|
||||
<div class="info-grid">
|
||||
<div class="info-item">
|
||||
<div class="info-label">诞生年份</div>
|
||||
<div class="info-value">{{ currentLang.year }}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">编程范式</div>
|
||||
<div class="info-value">{{ currentLang.paradigm }}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">类型系统</div>
|
||||
<div class="info-value">{{ currentLang.typeSystem }}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">性能</div>
|
||||
<div class="info-value">{{ currentLang.performance }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="use-cases">
|
||||
<div class="cases-title">主要用途</div>
|
||||
<div class="cases-list">
|
||||
<span
|
||||
v-for="(use, index) in currentLang.uses"
|
||||
:key="index"
|
||||
class="case-tag"
|
||||
>
|
||||
{{ use }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pros-cons">
|
||||
<div class="pros">
|
||||
<div class="list-title">✓ 优点</div>
|
||||
<ul>
|
||||
<li v-for="(pro, index) in currentLang.pros" :key="index">{{ pro }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="cons">
|
||||
<div class="list-title">✗ 缺点</div>
|
||||
<ul>
|
||||
<li v-for="(con, index) in currentLang.cons" :key="index">{{ con }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 快速对比表 -->
|
||||
<div class="quick-comparison">
|
||||
<div class="comparison-title">快速对比</div>
|
||||
<table class="comparison-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>语言</th>
|
||||
<th>学习难度</th>
|
||||
<th>开发效率</th>
|
||||
<th>执行性能</th>
|
||||
<th>主要领域</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(lang, index) in languages" :key="index" :class="{ highlighted: lang.name === activeLang }">
|
||||
<td>{{ lang.icon }} {{ lang.name }}</td>
|
||||
<td>{{ lang.difficulty }}</td>
|
||||
<td>{{ lang.efficiency }}</td>
|
||||
<td>{{ lang.performance }}</td>
|
||||
<td>{{ lang.mainField }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const activeLang = ref('Python')
|
||||
|
||||
const languages = [
|
||||
{
|
||||
name: 'Python',
|
||||
icon: '🐍',
|
||||
year: '1991',
|
||||
paradigm: '多范式',
|
||||
typeSystem: '动态强类型',
|
||||
performance: '中等',
|
||||
difficulty: '⭐',
|
||||
efficiency: '⭐⭐⭐⭐⭐',
|
||||
mainField: 'AI/数据/后端',
|
||||
uses: ['人工智能', '数据分析', 'Web 后端', '自动化脚本'],
|
||||
pros: ['语法简洁优雅', '生态丰富', '学习曲线平缓', '社区活跃'],
|
||||
cons: ['执行速度较慢', '移动端支持弱', 'GIL 限制并发']
|
||||
},
|
||||
{
|
||||
name: 'JavaScript',
|
||||
icon: '📜',
|
||||
year: '1995',
|
||||
paradigm: '多范式',
|
||||
typeSystem: '动态弱类型',
|
||||
performance: '中等',
|
||||
difficulty: '⭐⭐',
|
||||
efficiency: '⭐⭐⭐⭐',
|
||||
mainField: 'Web 开发',
|
||||
uses: ['前端开发', 'Node.js 后端', '跨平台应用', '小程序'],
|
||||
pros: ['无处不在', '生态庞大', '异步编程优秀', '跨平台'],
|
||||
cons: ['类型不安全', '标准混乱', '性能不如编译语言']
|
||||
},
|
||||
{
|
||||
name: 'Java',
|
||||
icon: '☕',
|
||||
year: '1995',
|
||||
paradigm: '面向对象',
|
||||
typeSystem: '静态强类型',
|
||||
performance: '高',
|
||||
difficulty: '⭐⭐⭐',
|
||||
efficiency: '⭐⭐⭐',
|
||||
mainField: '企业级应用',
|
||||
uses: ['企业后端', 'Android 应用', '大数据', '桌面应用'],
|
||||
pros: ['跨平台', '稳定可靠', '生态系统完善', '类型安全'],
|
||||
cons: ['语法繁琐', '内存占用大', '启动慢']
|
||||
},
|
||||
{
|
||||
name: 'C/C++',
|
||||
icon: '⚙️',
|
||||
year: '1972/1983',
|
||||
paradigm: '多范式',
|
||||
typeSystem: '静态强类型',
|
||||
performance: '极高',
|
||||
difficulty: '⭐⭐⭐⭐⭐',
|
||||
efficiency: '⭐⭐',
|
||||
mainField: '系统编程',
|
||||
uses: ['操作系统', '游戏引擎', '嵌入式', '高性能计算'],
|
||||
pros: ['性能极致', '底层控制力强', '效率高'],
|
||||
cons: ['学习困难', '内存管理复杂', '开发效率低', '容易出错']
|
||||
},
|
||||
{
|
||||
name: 'Go',
|
||||
icon: '🐹',
|
||||
year: '2009',
|
||||
paradigm: '多范式',
|
||||
typeSystem: '静态强类型',
|
||||
performance: '高',
|
||||
difficulty: '⭐⭐',
|
||||
efficiency: '⭐⭐⭐⭐',
|
||||
mainField: '云原生/后端',
|
||||
uses: ['云原生', '微服务', 'DevOps', '网络服务'],
|
||||
pros: ['简洁高效', '并发优秀', '编译快', '部署简单'],
|
||||
cons: ['生态较新', '缺少泛型(旧版)', '错误处理繁琐']
|
||||
},
|
||||
{
|
||||
name: 'Rust',
|
||||
icon: '🦀',
|
||||
year: '2010',
|
||||
paradigm: '多范式',
|
||||
typeSystem: '静态强类型',
|
||||
performance: '极高',
|
||||
difficulty: '⭐⭐⭐⭐',
|
||||
efficiency: '⭐⭐⭐',
|
||||
mainField: '系统/WebAssembly',
|
||||
uses: ['系统编程', 'WebAssembly', '区块链', 'CLI 工具'],
|
||||
pros: ['内存安全', '性能极高', '现代工具链'],
|
||||
cons: ['学习曲线陡', '编译速度慢', '生态尚在发展']
|
||||
}
|
||||
]
|
||||
|
||||
const currentLang = computed(() => languages.find(l => l.name === activeLang.value))
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.programming-language-comparison-demo {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.demo-header .icon { font-size: 1.5rem; }
|
||||
.demo-header .title { font-weight: 700; font-size: 1.1rem; }
|
||||
.demo-header .subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
|
||||
|
||||
.comparison-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
gap: 1rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.lang-card {
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.lang-card:hover {
|
||||
border-color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.lang-card.active {
|
||||
border-color: var(--vp-c-brand);
|
||||
background: var(--vp-c-brand-soft);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.card-icon {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.card-name {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.card-year {
|
||||
font-size: 0.75rem;
|
||||
color: var(--vp-c-text-2);
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.card-paradigm {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.detail-comparison {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.detail-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 1.5rem;
|
||||
padding-bottom: 1rem;
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.detail-icon {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.detail-name {
|
||||
font-weight: 600;
|
||||
font-size: 1.1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.detail-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.info-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
text-align: center;
|
||||
padding: 0.75rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-size: 0.75rem;
|
||||
color: var(--vp-c-text-2);
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.use-cases {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.cases-title {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.cases-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.75rem;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.case-tag {
|
||||
padding: 0.5rem 0.75rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 20px;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.pros-cons {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.pros-cons {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.pros,
|
||||
.cons {
|
||||
padding: 1rem;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.pros {
|
||||
background: rgba(16, 185, 129, 0.1);
|
||||
border: 1px solid #10b981;
|
||||
}
|
||||
|
||||
.cons {
|
||||
background: rgba(239, 68, 68, 0.1);
|
||||
border: 1px solid #ef4444;
|
||||
}
|
||||
|
||||
.list-title {
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.pros ul,
|
||||
.cons ul {
|
||||
margin: 0;
|
||||
padding-left: 1.25rem;
|
||||
}
|
||||
|
||||
.pros li,
|
||||
.cons li {
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.quick-comparison {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.comparison-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.comparison-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.comparison-table th {
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
padding: 0.75rem;
|
||||
text-align: center;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.comparison-table td {
|
||||
padding: 0.75rem;
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
text-align: center;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
tr.highlighted {
|
||||
background: var(--vp-c-brand-soft);
|
||||
}
|
||||
</style>
|
||||
+418
@@ -0,0 +1,418 @@
|
||||
<template>
|
||||
<div class="programming-paradigm-demo">
|
||||
<div class="demo-header">
|
||||
<span class="icon">🎨</span>
|
||||
<span class="title">编程范式</span>
|
||||
<span class="subtitle">不同的编程思维方式</span>
|
||||
</div>
|
||||
|
||||
<div class="paradigm-intro">
|
||||
编程范式是编程的<strong>思维方式</strong>,决定了如何组织和编写代码
|
||||
</div>
|
||||
|
||||
<div class="paradigm-cards">
|
||||
<div
|
||||
v-for="paradigm in paradigms"
|
||||
:key="paradigm.id"
|
||||
:class="['paradigm-card', { active: activeParadigm === paradigm.id }]"
|
||||
@click="activeParadigm = paradigm.id"
|
||||
>
|
||||
<div class="card-icon">{{ paradigm.icon }}</div>
|
||||
<div class="card-name">{{ paradigm.name }}</div>
|
||||
<div class="card-desc">{{ paradigm.desc }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 详细说明 -->
|
||||
<div v-if="activeParadigm" class="paradigm-detail">
|
||||
<div class="detail-header">
|
||||
<span class="detail-icon">{{ currentParadigm.icon }}</span>
|
||||
<span class="detail-title">{{ currentParadigm.name }}</span>
|
||||
</div>
|
||||
|
||||
<div class="detail-content">
|
||||
<div class="detail-section">
|
||||
<div class="section-title">核心思想</div>
|
||||
<div class="section-text">{{ currentParadigm.idea }}</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-section">
|
||||
<div class="section-title">代码示例</div>
|
||||
<div class="code-box">
|
||||
<pre><code>{{ currentParadigm.example }}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-section">
|
||||
<div class="section-title">特点</div>
|
||||
<div class="feature-tags">
|
||||
<span
|
||||
v-for="(feature, index) in currentParadigm.features"
|
||||
:key="index"
|
||||
class="feature-tag"
|
||||
>
|
||||
{{ feature }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-section">
|
||||
<div class="section-title">代表语言</div>
|
||||
<div class="lang-list">
|
||||
<span
|
||||
v-for="(lang, index) in currentParadigm.languages"
|
||||
:key="index"
|
||||
class="lang-item"
|
||||
>
|
||||
{{ lang }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 范式对比 -->
|
||||
<div class="paradigm-comparison">
|
||||
<div class="comparison-title">范式对比</div>
|
||||
<table class="comparison-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>特点</th>
|
||||
<th>命令式</th>
|
||||
<th>面向对象</th>
|
||||
<th>函数式</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>关注点</td>
|
||||
<td>怎么做</td>
|
||||
<td>谁来做</td>
|
||||
<td>做什么</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>数据管理</td>
|
||||
<td>变量和状态</td>
|
||||
<td>对象封装</td>
|
||||
<td>不可变数据</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>代码组织</td>
|
||||
<td>语句和函数</td>
|
||||
<td>类和对象</td>
|
||||
<td>纯函数</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>适用场景</td>
|
||||
<td>系统编程</td>
|
||||
<td>大型应用</td>
|
||||
<td>数据处理</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- 多范式 -->
|
||||
<div class="multi-paradigm">
|
||||
<div class="multi-title">现代多范式语言</div>
|
||||
<div class="multi-desc">
|
||||
大多数现代语言支持多种范式,开发者可以根据需求灵活选择
|
||||
</div>
|
||||
<div class="lang-grid">
|
||||
<div class="multi-lang-card">
|
||||
<div class="lang-name">Python</div>
|
||||
<div class="lang-paradigms">命令式 + 面向对象 + 函数式</div>
|
||||
</div>
|
||||
<div class="multi-lang-card">
|
||||
<div class="lang-name">JavaScript</div>
|
||||
<div class="lang-paradigms">命令式 + 面向对象 + 函数式</div>
|
||||
</div>
|
||||
<div class="multi-lang-card">
|
||||
<div class="lang-name">Rust</div>
|
||||
<div class="lang-paradigms">命令式 + 面向对象 + 函数式</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const activeParadigm = ref('imperative')
|
||||
|
||||
const paradigms = [
|
||||
{
|
||||
id: 'imperative',
|
||||
name: '命令式编程',
|
||||
icon: '📋',
|
||||
desc: '告诉计算机怎么做',
|
||||
idea: '通过一系列命令(语句)来改变程序状态,关注"怎么做"',
|
||||
example: '// 计算1-10的和\nlet sum = 0;\nfor (let i = 1; i <= 10; i++) {\n sum += i;\n}',
|
||||
features: ['变量', '循环', '条件判断', '语句'],
|
||||
languages: ['C', 'Python', 'JavaScript']
|
||||
},
|
||||
{
|
||||
id: 'oop',
|
||||
name: '面向对象编程',
|
||||
icon: '🎯',
|
||||
desc: '用对象来组织代码',
|
||||
idea: '将数据和操作数据的方法封装成对象,通过对象间交互来完成任务',
|
||||
example: 'class Calculator {\n add(a, b) { return a + b; }\n}\nconst calc = new Calculator();',
|
||||
features: ['封装', '继承', '多态', '类'],
|
||||
languages: ['Java', 'C++', 'Python', 'Ruby']
|
||||
},
|
||||
{
|
||||
id: 'functional',
|
||||
name: '函数式编程',
|
||||
icon: 'λ',
|
||||
desc: '函数是核心',
|
||||
idea: '强调纯函数、不可变数据,避免副作用,关注"做什么"',
|
||||
example: '// 计算1-10的和\nconst sum = Array.from(\n {length: 10}, (_, i) => i + 1\n).reduce((a, b) => a + b, 0);',
|
||||
features: ['纯函数', '不可变性', '高阶函数', '无副作用'],
|
||||
languages: ['Haskell', 'F#', 'Erlang', 'Clojure']
|
||||
}
|
||||
]
|
||||
|
||||
const currentParadigm = computed(() => paradigms.find(p => p.id === activeParadigm.value))
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.programming-paradigm-demo {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.demo-header .icon { font-size: 1.5rem; }
|
||||
.demo-header .title { font-weight: 700; font-size: 1.1rem; }
|
||||
.demo-header .subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
|
||||
|
||||
.paradigm-intro {
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg);
|
||||
border-left: 4px solid var(--vp-c-brand);
|
||||
border-radius: 6px;
|
||||
margin-bottom: 2rem;
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.paradigm-cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 1rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.paradigm-card {
|
||||
padding: 1.25rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.paradigm-card:hover {
|
||||
border-color: var(--vp-c-brand);
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
|
||||
.paradigm-card.active {
|
||||
border-color: var(--vp-c-brand);
|
||||
background: var(--vp-c-brand-soft);
|
||||
}
|
||||
|
||||
.card-icon {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.card-name {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.card-desc {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.paradigm-detail {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.detail-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 1.5rem;
|
||||
padding-bottom: 1rem;
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.detail-icon {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.detail-title {
|
||||
font-weight: 600;
|
||||
font-size: 1.1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.detail-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.detail-section {}
|
||||
|
||||
.section-title {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 0.75rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.section-text {
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.code-box {
|
||||
background: #1e1e1e;
|
||||
border-radius: 6px;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.code-box pre {
|
||||
margin: 0;
|
||||
color: #d4d4d4;
|
||||
font-size: 0.8rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.feature-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.feature-tag {
|
||||
padding: 0.5rem 0.75rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 20px;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.lang-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.lang-item {
|
||||
padding: 0.5rem 1rem;
|
||||
background: var(--vp-c-brand-soft);
|
||||
border: 1px solid var(--vp-c-brand);
|
||||
border-radius: 6px;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.paradigm-comparison {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.comparison-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.comparison-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.comparison-table th {
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
padding: 0.75rem;
|
||||
text-align: center;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.comparison-table td {
|
||||
padding: 0.75rem;
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
text-align: center;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.multi-paradigm {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.multi-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 0.75rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.multi-desc {
|
||||
font-size: 0.9rem;
|
||||
color: var(--vp-c-text-2);
|
||||
margin-bottom: 1rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.lang-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.multi-lang-card {
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 6px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.lang-name {
|
||||
font-weight: 700;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 0.5rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.lang-paradigms {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
</style>
|
||||
+531
@@ -0,0 +1,531 @@
|
||||
<template>
|
||||
<div class="recursive-thinking-demo">
|
||||
<div class="demo-header">
|
||||
<span class="icon">🔄</span>
|
||||
<span class="title">递归思维:自己调用自己</span>
|
||||
<span class="subtitle">把大问题分解成相同的小问题</span>
|
||||
</div>
|
||||
|
||||
<div class="analogy-section">
|
||||
<div class="analogy-box">
|
||||
<div class="analogy-icon">🪆</div>
|
||||
<div class="analogy-content">
|
||||
<div class="analogy-title">俄罗斯套娃</div>
|
||||
<div class="analogy-desc">
|
||||
打开一个大娃娃,里面有个小一点的娃娃<br>
|
||||
再打开还有更小的...直到最小的一个<br>
|
||||
<strong>这就是递归!</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="example-selector">
|
||||
<div class="selector-title">递归示例</div>
|
||||
<div class="selector-buttons">
|
||||
<button
|
||||
v-for="example in examples"
|
||||
:key="example.id"
|
||||
:class="['example-btn', { active: activeExample === example.id }]"
|
||||
@click="activeExample = example.id"
|
||||
>
|
||||
{{ example.icon }} {{ example.name }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 阶乘示例 -->
|
||||
<div v-if="activeExample === 'factorial'" class="example-content">
|
||||
<div class="example-title">阶乘:n! = n × (n-1)!</div>
|
||||
<div class="factorial-visual">
|
||||
<div class="factorial-call">
|
||||
<div class="call-box">5! = 5 × 4!</div>
|
||||
<div class="call-arrow">↓</div>
|
||||
<div class="call-box">4! = 4 × 3!</div>
|
||||
<div class="call-arrow">↓</div>
|
||||
<div class="call-box">3! = 3 × 2!</div>
|
||||
<div class="call-arrow">↓</div>
|
||||
<div class="call-box">2! = 2 × 1!</div>
|
||||
<div class="call-arrow">↓</div>
|
||||
<div class="call-box base">1! = 1 (基准情况)</div>
|
||||
</div>
|
||||
<div class="factorial-return">
|
||||
<div class="return-arrow">↑ 返回 1</div>
|
||||
<div class="return-arrow">↑ 返回 2 × 1 = 2</div>
|
||||
<div class="return-arrow">↑ 返回 3 × 2 = 6</div>
|
||||
<div class="return-arrow">↑ 返回 4 × 6 = 24</div>
|
||||
<div class="return-arrow">↑ 返回 5 × 24 = 120</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 斐波那契示例 -->
|
||||
<div v-if="activeExample === 'fibonacci'" class="example-content">
|
||||
<div class="example-title">斐波那契数列</div>
|
||||
<div class="fibonacci-visual">
|
||||
<div class="fib-rule">F(n) = F(n-1) + F(n-2)</div>
|
||||
<div class="fib-tree">
|
||||
<div class="tree-node">F(5)</div>
|
||||
<div class="tree-level">
|
||||
<div class="tree-node">F(4)</div>
|
||||
<div class="tree-node">F(3)</div>
|
||||
</div>
|
||||
<div class="tree-level">
|
||||
<div class="tree-node">F(3)</div>
|
||||
<div class="tree-node">F(2)</div>
|
||||
<div class="tree-node">F(2)</div>
|
||||
<div class="tree-node">F(1)=1</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fib-result">F(5) = 5</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 目录遍历示例 -->
|
||||
<div v-if="activeExample === 'directory'" class="example-content">
|
||||
<div class="example-title">遍历文件目录</div>
|
||||
<div class="directory-visual">
|
||||
<div class="dir-tree">
|
||||
<div class="dir-node root">📁 /home</div>
|
||||
<div class="dir-children">
|
||||
<div class="dir-node">📄 file1.txt</div>
|
||||
<div class="dir-node">📁 documents</div>
|
||||
<div class="dir-children">
|
||||
<div class="dir-node">📄 report.doc</div>
|
||||
<div class="dir-node">📁 photos</div>
|
||||
<div class="dir-children">
|
||||
<div class="dir-node">🖼️ pic1.jpg</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dir-node">📄 file2.txt</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dir-pseudocode">
|
||||
<div class="pseudo-title">伪代码</div>
|
||||
<pre>function traverse(folder) {
|
||||
for each item in folder {
|
||||
if item is file {
|
||||
print(item)
|
||||
} else if item is folder {
|
||||
traverse(item) // 递归调用!
|
||||
}
|
||||
}
|
||||
}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 递归三要素 -->
|
||||
<div class="recursive-elements">
|
||||
<div class="elements-title">递归的三要素</div>
|
||||
<div class="elements-grid">
|
||||
<div class="element-card">
|
||||
<div class="element-number">1</div>
|
||||
<div class="element-title">基准情况</div>
|
||||
<div class="element-desc">什么时候停止递归?必须有一个终止条件</div>
|
||||
<div class="element-example">例:n! 中 1! = 1</div>
|
||||
</div>
|
||||
<div class="element-card">
|
||||
<div class="element-number">2</div>
|
||||
<div class="element-title">递归调用</div>
|
||||
<div class="element-desc">如何让问题规模变小?调用自己处理更小的规模</div>
|
||||
<div class="element-example">例:n! 转换成 (n-1)!</div>
|
||||
</div>
|
||||
<div class="element-card">
|
||||
<div class="element-number">3</div>
|
||||
<div class="element-title">返回结果</div>
|
||||
<div class="element-desc">如何利用子问题的结果解决当前问题?</div>
|
||||
<div class="element-example">例:n × (n-1)! 的结果</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 优缺点 -->
|
||||
<div class="pros-cons">
|
||||
<div class="pros-column">
|
||||
<div class="column-title">✓ 优点</div>
|
||||
<ul class="column-list">
|
||||
<li>代码简洁优雅</li>
|
||||
<li>自然表达递归结构</li>
|
||||
<li>适合树和图的遍历</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="cons-column">
|
||||
<div class="column-title">✗ 缺点</div>
|
||||
<ul class="column-list">
|
||||
<li>可能重复计算</li>
|
||||
<li>栈空间消耗大</li>
|
||||
<li>调试较困难</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const activeExample = ref('factorial')
|
||||
|
||||
const examples = [
|
||||
{ id: 'factorial', name: '阶乘', icon: '🔢' },
|
||||
{ id: 'fibonacci', name: '斐波那契', icon: '🐚' },
|
||||
{ id: 'directory', name: '目录遍历', icon: '📁' }
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.recursive-thinking-demo {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.demo-header .icon { font-size: 1.5rem; }
|
||||
.demo-header .title { font-weight: 700; font-size: 1.1rem; }
|
||||
.demo-header .subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
|
||||
|
||||
.analogy-section {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.analogy-box {
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
padding: 1.5rem;
|
||||
background: var(--vp-c-bg);
|
||||
border-left: 4px solid var(--vp-c-brand);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.analogy-icon {
|
||||
font-size: 3rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.analogy-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.analogy-title {
|
||||
font-weight: 600;
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: 0.5rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.analogy-desc {
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.example-selector {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.selector-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 0.75rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.selector-buttons {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.example-btn {
|
||||
padding: 0.6rem 1rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 0.9rem;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.example-btn:hover {
|
||||
border-color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.example-btn.active {
|
||||
background: var(--vp-c-brand);
|
||||
border-color: var(--vp-c-brand);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.example-content {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.example-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
text-align: center;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.factorial-visual {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.factorial-visual {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.call-box {
|
||||
padding: 0.75rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
text-align: center;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.call-box.base {
|
||||
background: #10b981;
|
||||
color: white;
|
||||
border-color: #10b981;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.call-arrow {
|
||||
text-align: center;
|
||||
font-size: 1.5rem;
|
||||
color: var(--vp-c-brand);
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.return-arrow {
|
||||
padding: 0.5rem;
|
||||
background: rgba(59, 130, 246, 0.1);
|
||||
border: 1px solid #3b82f6;
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
font-size: 0.85rem;
|
||||
color: #3b82f6;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.fibonacci-visual {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.fib-rule {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-brand);
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.fib-tree {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.tree-level {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.tree-node {
|
||||
padding: 0.5rem 1rem;
|
||||
background: var(--vp-c-brand-soft);
|
||||
border: 1px solid var(--vp-c-brand);
|
||||
border-radius: 6px;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.fib-result {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 700;
|
||||
color: #10b981;
|
||||
}
|
||||
|
||||
.directory-visual {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.directory-visual {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.dir-tree {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.dir-node {
|
||||
padding: 0.5rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 4px;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.dir-node.root {
|
||||
background: var(--vp-c-brand-soft);
|
||||
border-color: var(--vp-c-brand);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.dir-children {
|
||||
margin-left: 1.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.dir-pseudocode {
|
||||
background: #1e1e1e;
|
||||
border-radius: 6px;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.pseudo-title {
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.dir-pseudocode pre {
|
||||
color: #d4d4d4;
|
||||
font-size: 0.8rem;
|
||||
line-height: 1.6;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.recursive-elements {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.elements-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.elements-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.element-card {
|
||||
padding: 1.25rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.element-number {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.element-title {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.element-desc {
|
||||
font-size: 0.85rem;
|
||||
color: var(--vp-c-text-1);
|
||||
line-height: 1.5;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.element-example {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.pros-cons {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.pros-cons {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.pros-column,
|
||||
.cons-column {
|
||||
padding: 1.25rem;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.pros-column {
|
||||
background: rgba(16, 185, 129, 0.1);
|
||||
border: 1px solid #10b981;
|
||||
}
|
||||
|
||||
.cons-column {
|
||||
background: rgba(239, 68, 68, 0.1);
|
||||
border: 1px solid #ef4444;
|
||||
}
|
||||
|
||||
.column-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.column-list {
|
||||
margin: 0;
|
||||
padding-left: 1.25rem;
|
||||
}
|
||||
|
||||
.column-list li {
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.8;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
</style>
|
||||
+368
@@ -0,0 +1,368 @@
|
||||
<template>
|
||||
<div class="search-algorithm-demo">
|
||||
<div class="demo-header">
|
||||
<span class="icon">🔍</span>
|
||||
<span class="title">查找算法</span>
|
||||
<span class="subtitle">如何在数据中找到目标</span>
|
||||
</div>
|
||||
|
||||
<div class="algorithm-selector">
|
||||
<button
|
||||
:class="['algo-btn', { active: activeAlgo === 'linear' }]"
|
||||
@click="activeAlgo = 'linear'"
|
||||
>
|
||||
顺序查找
|
||||
</button>
|
||||
<button
|
||||
:class="['algo-btn', { active: activeAlgo === 'binary' }]"
|
||||
@click="activeAlgo = 'binary'"
|
||||
>
|
||||
二分查找
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 顺序查找 -->
|
||||
<div v-if="activeAlgo === 'linear'" class="algo-content">
|
||||
<div class="content-title">顺序查找:一个一个找</div>
|
||||
<div class="linear-demo">
|
||||
<div class="search-array">
|
||||
<div
|
||||
v-for="(num, index) in numbers"
|
||||
:key="index"
|
||||
:class="['array-cell', { found: index === foundIndex, searching: index <= searchStep && searching }]"
|
||||
>
|
||||
{{ num }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="search-controls">
|
||||
<button @click="startLinearSearch" class="search-btn">开始查找</button>
|
||||
<button @click="reset" class="reset-btn">重置</button>
|
||||
</div>
|
||||
<div class="search-info">
|
||||
目标数字:<input v-model="targetNumber" type="number" class="target-input" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="algo-stats">
|
||||
<div class="stat-item">时间复杂度:O(n)</div>
|
||||
<div class="stat-item">适用:无序数组</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 二分查找 -->
|
||||
<div v-if="activeAlgo === 'binary'" class="algo-content">
|
||||
<div class="content-title">二分查找:每次排除一半</div>
|
||||
<div class="binary-demo">
|
||||
<div class="sorted-array">
|
||||
<div
|
||||
v-for="(num, index) in sortedNumbers"
|
||||
:key="index"
|
||||
:class="['array-cell', {
|
||||
found: index === binaryFoundIndex,
|
||||
left: index >= binaryLeft && index <= binaryRight,
|
||||
eliminated: index < binaryLeft || index > binaryRight
|
||||
}]"
|
||||
>
|
||||
{{ num }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="binary-info">
|
||||
<div class="info-step">查找范围:[{{ binaryLeft }}, {{ binaryRight }}]</div>
|
||||
<div class="info-mid">中间位置:{{ binaryMid }}</div>
|
||||
<div class="info-comparison">{{ sortedNumbers[binaryMid] }} vs {{ binaryTarget }}</div>
|
||||
</div>
|
||||
<div class="search-controls">
|
||||
<button @click="binaryStep" class="search-btn">下一步</button>
|
||||
<button @click="resetBinary" class="reset-btn">重置</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="algo-stats">
|
||||
<div class="stat-item">时间复杂度:O(log n)</div>
|
||||
<div class="stat-item">适用:有序数组</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 对比 -->
|
||||
<div class="comparison">
|
||||
<div class="comparison-title">性能对比</div>
|
||||
<table class="comparison-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>数据量</th>
|
||||
<th>顺序查找</th>
|
||||
<th>二分查找</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="n in [10, 100, 1000, 10000]" :key="n">
|
||||
<td>{{ n }}</td>
|
||||
<td>最多 {{ n }} 次</td>
|
||||
<td>最多 {{ Math.ceil(Math.log2(n)) }} 次</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const activeAlgo = ref('linear')
|
||||
const targetNumber = ref(7)
|
||||
const foundIndex = ref(-1)
|
||||
const searchStep = ref(-1)
|
||||
const searching = ref(false)
|
||||
|
||||
const numbers = ref([3, 7, 2, 9, 5, 1, 8, 4, 6, 10])
|
||||
|
||||
const sortedNumbers = ref([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
|
||||
const binaryTarget = ref(7)
|
||||
const binaryLeft = ref(0)
|
||||
const binaryRight = ref(9)
|
||||
const binaryMid = ref(4)
|
||||
const binaryFoundIndex = ref(-1)
|
||||
|
||||
const startLinearSearch = () => {
|
||||
searching.value = true
|
||||
searchStep.value = -1
|
||||
foundIndex.value = -1
|
||||
|
||||
let step = 0
|
||||
const interval = setInterval(() => {
|
||||
if (step < numbers.value.length) {
|
||||
searchStep.value = step
|
||||
if (numbers.value[step] === targetNumber.value) {
|
||||
foundIndex.value = step
|
||||
searching.value = false
|
||||
clearInterval(interval)
|
||||
}
|
||||
step++
|
||||
} else {
|
||||
searching.value = false
|
||||
clearInterval(interval)
|
||||
}
|
||||
}, 500)
|
||||
}
|
||||
|
||||
const reset = () => {
|
||||
searchStep.value = -1
|
||||
foundIndex.value = -1
|
||||
searching.value = false
|
||||
}
|
||||
|
||||
const binaryStep = () => {
|
||||
binaryMid.value = Math.floor((binaryLeft.value + binaryRight.value) / 2)
|
||||
|
||||
if (sortedNumbers.value[binaryMid.value] === binaryTarget.value) {
|
||||
binaryFoundIndex.value = binaryMid.value
|
||||
} else if (sortedNumbers.value[binaryMid.value] < binaryTarget.value) {
|
||||
binaryLeft.value = binaryMid.value + 1
|
||||
} else {
|
||||
binaryRight.value = binaryMid.value - 1
|
||||
}
|
||||
}
|
||||
|
||||
const resetBinary = () => {
|
||||
binaryLeft.value = 0
|
||||
binaryRight.value = 9
|
||||
binaryMid.value = 4
|
||||
binaryFoundIndex.value = -1
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.search-algorithm-demo {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.demo-header .icon { font-size: 1.5rem; }
|
||||
.demo-header .title { font-weight: 700; font-size: 1.1rem; }
|
||||
.demo-header .subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
|
||||
|
||||
.algorithm-selector {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.algo-btn {
|
||||
flex: 1;
|
||||
padding: 0.75rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
font-size: 0.9rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.algo-btn.active {
|
||||
background: var(--vp-c-brand);
|
||||
border-color: var(--vp-c-brand);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.algo-content {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.content-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
text-align: center;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.search-array,
|
||||
.sorted-array {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.array-cell {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.array-cell.searching {
|
||||
border-color: #f59e0b;
|
||||
background: rgba(245, 158, 11, 0.1);
|
||||
}
|
||||
|
||||
.array-cell.found {
|
||||
border-color: #10b981;
|
||||
background: #10b981;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.array-cell.left {
|
||||
border-color: var(--vp-c-brand);
|
||||
background: var(--vp-c-brand-soft);
|
||||
}
|
||||
|
||||
.array-cell.eliminated {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.search-controls {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: center;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.search-btn {
|
||||
padding: 0.6rem 1.25rem;
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-size: 0.9rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.reset-btn {
|
||||
padding: 0.6rem 1.25rem;
|
||||
background: var(--vp-c-divider);
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-size: 0.9rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.search-info {
|
||||
text-align: center;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.target-input {
|
||||
width: 60px;
|
||||
padding: 0.5rem;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.binary-info {
|
||||
text-align: center;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.info-step,
|
||||
.info-mid,
|
||||
.info-comparison {
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.algo-stats {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: center;
|
||||
padding-top: 1rem;
|
||||
border-top: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
font-size: 0.85rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.comparison {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.comparison-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.comparison-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.comparison-table th {
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
padding: 0.75rem;
|
||||
text-align: center;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.comparison-table td {
|
||||
padding: 0.75rem;
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
text-align: center;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
</style>
|
||||
+319
@@ -0,0 +1,319 @@
|
||||
<template>
|
||||
<div class="sorting-algorithm-demo">
|
||||
<div class="demo-header">
|
||||
<span class="icon">📊</span>
|
||||
<span class="title">排序算法</span>
|
||||
<span class="subtitle">把数据按顺序排列</span>
|
||||
</div>
|
||||
|
||||
<div class="visual-array">
|
||||
<div
|
||||
v-for="(item, index) in array"
|
||||
:key="index"
|
||||
class="array-bar"
|
||||
:class="{
|
||||
comparing: comparingIndices.includes(index),
|
||||
swapping: swappingIndices.includes(index),
|
||||
sorted: index < sortedCount
|
||||
}"
|
||||
:style="{ height: item * 3 + 'px' }"
|
||||
>
|
||||
{{ item }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<button @click="generateArray" class="control-btn">生成新数组</button>
|
||||
<button @click="startBubbleSort" class="control-btn">冒泡排序</button>
|
||||
<button @click="startQuickSort" class="control-btn">快速排序</button>
|
||||
</div>
|
||||
|
||||
<div class="algorithm-info">
|
||||
<div class="info-title">{{ currentAlgo }}</div>
|
||||
<div class="info-desc">{{ currentAlgoDesc }}</div>
|
||||
<div class="info-complexity">时间复杂度:{{ complexity }}</div>
|
||||
</div>
|
||||
|
||||
<div class="comparison">
|
||||
<div class="comparison-title">算法对比</div>
|
||||
<table class="comparison-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>算法</th>
|
||||
<th>平均时间</th>
|
||||
<th>最坏时间</th>
|
||||
<th>空间</th>
|
||||
<th>稳定</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>冒泡排序</td>
|
||||
<td>O(n²)</td>
|
||||
<td>O(n²)</td>
|
||||
<td>O(1)</td>
|
||||
<td>✓</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>快速排序</td>
|
||||
<td>O(n log n)</td>
|
||||
<td>O(n²)</td>
|
||||
<td>O(log n)</td>
|
||||
<td>✗</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>归并排序</td>
|
||||
<td>O(n log n)</td>
|
||||
<td>O(n log n)</td>
|
||||
<td>O(n)</td>
|
||||
<td>✓</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>插入排序</td>
|
||||
<td>O(n²)</td>
|
||||
<td>O(n²)</td>
|
||||
<td>O(1)</td>
|
||||
<td>✓</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const array = ref([50, 30, 70, 40, 90, 20, 60, 80, 10, 55])
|
||||
const comparingIndices = ref([])
|
||||
const swappingIndices = ref([])
|
||||
const sortedCount = ref(0)
|
||||
const currentAlgo = ref('请选择排序算法')
|
||||
const currentAlgoDesc = ref('选择一个排序算法开始演示')
|
||||
const complexity = ref('')
|
||||
|
||||
const generateArray = () => {
|
||||
array.value = Array.from({ length: 10 }, () => Math.floor(Math.random() * 90) + 10)
|
||||
sortedCount.value = 0
|
||||
comparingIndices.value = []
|
||||
swappingIndices.value = []
|
||||
}
|
||||
|
||||
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms))
|
||||
|
||||
const startBubbleSort = async () => {
|
||||
currentAlgo.value = '冒泡排序'
|
||||
currentAlgoDesc.value = '重复遍历数组,比较相邻元素并交换'
|
||||
complexity.value = 'O(n²)'
|
||||
|
||||
sortedCount.value = 0
|
||||
const arr = [...array.value]
|
||||
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
for (let j = 0; j < arr.length - i - 1; j++) {
|
||||
comparingIndices.value = [j, j + 1]
|
||||
await sleep(300)
|
||||
|
||||
if (arr[j] > arr[j + 1]) {
|
||||
swappingIndices.value = [j, j + 1]
|
||||
await sleep(300)
|
||||
;[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]
|
||||
array.value = [...arr]
|
||||
await sleep(300)
|
||||
swappingIndices.value = []
|
||||
}
|
||||
}
|
||||
sortedCount.value++
|
||||
}
|
||||
|
||||
comparingIndices.value = []
|
||||
sortedCount.value = arr.length
|
||||
}
|
||||
|
||||
const startQuickSort = async () => {
|
||||
currentAlgo.value = '快速排序'
|
||||
currentAlgoDesc.value = '选择基准,将数组分成小于和大于基准的两部分'
|
||||
complexity.value = 'O(n log n)'
|
||||
|
||||
sortedCount.value = 0
|
||||
const arr = [...array.value]
|
||||
|
||||
await quickSort(arr, 0, arr.length - 1)
|
||||
array.value = arr
|
||||
sortedCount.value = arr.length
|
||||
comparingIndices.value = []
|
||||
}
|
||||
|
||||
const quickSort = async (arr, low, high) => {
|
||||
if (low < high) {
|
||||
const pi = await partition(arr, low, high)
|
||||
await quickSort(arr, low, pi - 1)
|
||||
await quickSort(arr, pi + 1, high)
|
||||
}
|
||||
}
|
||||
|
||||
const partition = async (arr, low, high) => {
|
||||
const pivot = arr[high]
|
||||
let i = low - 1
|
||||
|
||||
for (let j = low; j < high; j++) {
|
||||
comparingIndices.value = [j, high]
|
||||
await sleep(300)
|
||||
|
||||
if (arr[j] < pivot) {
|
||||
i++
|
||||
swappingIndices.value = [i, j]
|
||||
await sleep(300)
|
||||
;[arr[i], arr[j]] = [arr[j], arr[i]]
|
||||
array.value = [...arr]
|
||||
await sleep(300)
|
||||
}
|
||||
}
|
||||
|
||||
swappingIndices.value = [i + 1, high]
|
||||
await sleep(300)
|
||||
;[arr[i + 1], arr[high]] = [arr[high], arr[i + 1]]
|
||||
array.value = [...arr]
|
||||
await sleep(300)
|
||||
swappingIndices.value = []
|
||||
|
||||
return i + 1
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.sorting-algorithm-demo {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.demo-header .icon { font-size: 1.5rem; }
|
||||
.demo-header .title { font-weight: 700; font-size: 1.1rem; }
|
||||
.demo-header .subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
|
||||
|
||||
.visual-array {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
height: 300px;
|
||||
margin-bottom: 2rem;
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.array-bar {
|
||||
flex: 1;
|
||||
max-width: 50px;
|
||||
background: var(--vp-c-brand);
|
||||
border-radius: 4px 4px 0 0;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
padding-bottom: 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
color: white;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.array-bar.comparing {
|
||||
background: #f59e0b;
|
||||
}
|
||||
|
||||
.array-bar.swapping {
|
||||
background: #ef4444;
|
||||
}
|
||||
|
||||
.array-bar.sorted {
|
||||
background: #10b981;
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.control-btn {
|
||||
padding: 0.6rem 1.25rem;
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-size: 0.9rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.control-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.algorithm-info {
|
||||
text-align: center;
|
||||
padding: 1.5rem;
|
||||
background: var(--vp-c-bg);
|
||||
border-radius: 8px;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.info-title {
|
||||
font-weight: 600;
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: 0.5rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.info-desc {
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0.5rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.info-complexity {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 0.9rem;
|
||||
color: var(--vp-c-brand);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.comparison-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.comparison-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.comparison-table th {
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
padding: 0.75rem;
|
||||
text-align: center;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.comparison-table td {
|
||||
padding: 0.75rem;
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
text-align: center;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
</style>
|
||||
+246
@@ -0,0 +1,246 @@
|
||||
<template>
|
||||
<div class="storage-hierarchy-demo">
|
||||
<div class="demo-header">
|
||||
<span class="icon">💾</span>
|
||||
<span class="title">存储层次结构</span>
|
||||
<span class="subtitle">从快到慢,从小到大</span>
|
||||
</div>
|
||||
|
||||
<div class="hierarchy-pyramid">
|
||||
<div class="pyramid-level register">
|
||||
<div class="level-name">寄存器</div>
|
||||
<div class="level-speed">最快</div>
|
||||
<div class="level-size">最小 (KB)</div>
|
||||
</div>
|
||||
<div class="pyramid-level cache">
|
||||
<div class="level-name">缓存</div>
|
||||
<div class="level-speed">很快</div>
|
||||
<div class="level-size">小 (MB)</div>
|
||||
</div>
|
||||
<div class="pyramid-level ram">
|
||||
<div class="level-name">内存</div>
|
||||
<div class="level-speed">快</div>
|
||||
<div class="level-size">中等 (GB)</div>
|
||||
</div>
|
||||
<div class="pyramid-level disk">
|
||||
<div class="level-name">硬盘</div>
|
||||
<div class="level-speed">慢</div>
|
||||
<div class="level-size">大 (TB)</div>
|
||||
</div>
|
||||
<div class="pyramid-level network">
|
||||
<div class="level-name">网络/云</div>
|
||||
<div class="level-speed">最慢</div>
|
||||
<div class="level-size">无限</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="comparison-table">
|
||||
<div class="table-title">详细对比</div>
|
||||
<table class="hierarchy-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>存储层次</th>
|
||||
<th>访问时间</th>
|
||||
<th>典型容量</th>
|
||||
<th>成本</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>寄存器</td>
|
||||
<td>< 1 ns</td>
|
||||
<td>几 KB</td>
|
||||
<td>最高</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>L1 缓存</td>
|
||||
<td>~1 ns</td>
|
||||
<td>64 KB</td>
|
||||
<td>很高</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>L2 缓存</td>
|
||||
<td>~3 ns</td>
|
||||
<td>256 KB</td>
|
||||
<td>高</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>L3 缓存</td>
|
||||
<td>~10 ns</td>
|
||||
<td>8 MB</td>
|
||||
<td>中等</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>内存</td>
|
||||
<td>~100 ns</td>
|
||||
<td>8-32 GB</td>
|
||||
<td>中低</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SSD</td>
|
||||
<td>~100 μs</td>
|
||||
<td>256 GB-2 TB</td>
|
||||
<td>低</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>HDD</td>
|
||||
<td>~10 ms</td>
|
||||
<td>1-10 TB</td>
|
||||
<td>最低</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="principle">
|
||||
<div class="principle-title">局部性原理</div>
|
||||
<div class="principle-content">
|
||||
<div class="principle-text">
|
||||
程序倾向于访问<strong>最近访问过的位置</strong>(时间局部性)
|
||||
和<strong>邻近的位置</strong>(空间局部性)
|
||||
</div>
|
||||
<div class="principle-example">
|
||||
利用局部性原理,缓存可以显著提高性能
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.storage-hierarchy-demo {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.demo-header .icon { font-size: 1.5rem; }
|
||||
.demo-header .title { font-weight: 700; font-size: 1.1rem; }
|
||||
.demo-header .subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
|
||||
|
||||
.hierarchy-pyramid {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.pyramid-level {
|
||||
padding: 1rem;
|
||||
border-radius: 6px;
|
||||
text-align: center;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.pyramid-level.register {
|
||||
background: linear-gradient(135deg, #ef4444, #dc2626);
|
||||
}
|
||||
|
||||
.pyramid-level.cache {
|
||||
background: linear-gradient(135deg, #f59e0b, #d97706);
|
||||
}
|
||||
|
||||
.pyramid-level.ram {
|
||||
background: linear-gradient(135deg, #10b981, #059669);
|
||||
}
|
||||
|
||||
.pyramid-level.disk {
|
||||
background: linear-gradient(135deg, #3b82f6, #2563eb);
|
||||
}
|
||||
|
||||
.pyramid-level.network {
|
||||
background: linear-gradient(135deg, #8b5cf6, #7c3aed);
|
||||
}
|
||||
|
||||
.level-name {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.level-speed {
|
||||
font-size: 0.8rem;
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.level-size {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.comparison-table {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.table-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.hierarchy-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.hierarchy-table th {
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
padding: 0.75rem;
|
||||
text-align: center;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.hierarchy-table td {
|
||||
padding: 0.75rem;
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
text-align: center;
|
||||
font-size: 0.85rem;
|
||||
font-family: 'Courier New', monospace;
|
||||
}
|
||||
|
||||
.principle {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.principle-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.principle-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.principle-text {
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.principle-example {
|
||||
padding: 0.75rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 6px;
|
||||
font-size: 0.85rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
</style>
|
||||
+567
@@ -0,0 +1,567 @@
|
||||
<template>
|
||||
<div class="transport-layer-demo">
|
||||
<div class="demo-header">
|
||||
<span class="icon">🚚</span>
|
||||
<span class="title">传输层:端到端的可靠传输</span>
|
||||
<span class="subtitle">TCP 和 UDP 如何传输数据</span>
|
||||
</div>
|
||||
|
||||
<div class="protocol-tabs">
|
||||
<button
|
||||
:class="['tab-btn', { active: activeProtocol === 'tcp' }]"
|
||||
@click="activeProtocol = 'tcp'"
|
||||
>
|
||||
TCP 📦
|
||||
</button>
|
||||
<button
|
||||
:class="['tab-btn', { active: activeProtocol === 'udp' }]"
|
||||
@click="activeProtocol = 'udp'"
|
||||
>
|
||||
UDP ⚡
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 可视化演示 -->
|
||||
<div class="protocol-visual">
|
||||
<div class="visual-header">
|
||||
<span class="protocol-title">{{ currentProtocol.name }}</span>
|
||||
<span class="protocol-slogan">{{ currentProtocol.slogan }}</span>
|
||||
</div>
|
||||
|
||||
<div class="visual-content">
|
||||
<!-- TCP 可靠传输 -->
|
||||
<div v-if="activeProtocol === 'tcp'" class="tcp-demo">
|
||||
<div class="connection-stages">
|
||||
<div
|
||||
v-for="(stage, index) in tcpStages"
|
||||
:key="index"
|
||||
:class="['stage-item', { active: activeTcpStage === index }]"
|
||||
@click="activeTcpStage = index"
|
||||
>
|
||||
<div class="stage-number">{{ index + 1 }}</div>
|
||||
<div class="stage-text">{{ stage }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tcp-reliability">
|
||||
<div class="reliability-title">TCP 可靠性机制</div>
|
||||
<div class="mechanism-grid">
|
||||
<div
|
||||
v-for="(mech, index) in tcpMechanisms"
|
||||
:key="index"
|
||||
class="mechanism-card"
|
||||
>
|
||||
<div class="mech-icon">{{ mech.icon }}</div>
|
||||
<div class="mech-title">{{ mech.title }}</div>
|
||||
<div class="mech-desc">{{ mech.desc }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- UDP 快速传输 -->
|
||||
<div v-if="activeProtocol === 'udp'" class="udp-demo">
|
||||
<div class="udp-comparison">
|
||||
<div class="comparison-side tcp-side">
|
||||
<div class="side-header">TCP</div>
|
||||
<div class="side-animation">
|
||||
<div class="packet" v-for="i in 3" :key="'tcp-' + i">
|
||||
📦 {{ i }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="side-desc">三次握手 + 确认应答</div>
|
||||
</div>
|
||||
|
||||
<div class="vs-badge">VS</div>
|
||||
|
||||
<div class="comparison-side udp-side">
|
||||
<div class="side-header">UDP</div>
|
||||
<div class="side-animation">
|
||||
<div class="packet fast" v-for="i in 5" :key="'udp-' + i">
|
||||
⚡ {{ i }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="side-desc">直接发送,无等待</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="udp-use-cases">
|
||||
<div class="use-cases-title">UDP 适用场景</div>
|
||||
<div class="use-cases-grid">
|
||||
<div
|
||||
v-for="(use, index) in udpUseCases"
|
||||
:key="index"
|
||||
class="use-case-card"
|
||||
>
|
||||
<div class="use-icon">{{ use.icon }}</div>
|
||||
<div class="use-title">{{ use.title }}</div>
|
||||
<div class="use-reason">{{ use.reason }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 端口说明 -->
|
||||
<div class="port-section">
|
||||
<div class="port-title">端口号:应用程序的标识</div>
|
||||
<div class="port-examples">
|
||||
<div class="port-intro">
|
||||
端口号就像公寓房间号,IP 地址是公寓楼地址,合起来才能找到具体的应用程序
|
||||
</div>
|
||||
<div class="port-list">
|
||||
<div
|
||||
v-for="(port, index) in commonPorts"
|
||||
:key="index"
|
||||
class="port-item"
|
||||
>
|
||||
<div class="port-number">{{ port.number }}</div>
|
||||
<div class="port-service">{{ port.service }}</div>
|
||||
<div class="port-desc">{{ port.desc }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const activeProtocol = ref('tcp')
|
||||
const activeTcpStage = ref(0)
|
||||
|
||||
const protocolData = {
|
||||
tcp: {
|
||||
name: 'TCP:可靠传输协议',
|
||||
slogan: '像快递服务,确保每个包裹都送达'
|
||||
},
|
||||
udp: {
|
||||
name: 'UDP:快速传输协议',
|
||||
slogan: '像明信片,发送出去就不管了'
|
||||
}
|
||||
}
|
||||
|
||||
const tcpStages = [
|
||||
'建立连接(三次握手)',
|
||||
'数据传输(带序号和确认)',
|
||||
'连接关闭(四次挥手)'
|
||||
]
|
||||
|
||||
const tcpMechanisms = [
|
||||
{
|
||||
icon: '🤝',
|
||||
title: '三次握手',
|
||||
desc: '建立可靠连接,确保双方都准备好'
|
||||
},
|
||||
{
|
||||
icon: '🔢',
|
||||
title: '序号和确认',
|
||||
desc: '每个数据包都有编号,收到需要确认'
|
||||
},
|
||||
{
|
||||
icon: '🔁',
|
||||
title: '超时重传',
|
||||
desc: '未收到确认则自动重传丢失的数据'
|
||||
},
|
||||
{
|
||||
icon: '🚦',
|
||||
title: '流量控制',
|
||||
desc: '根据接收方能力调整发送速度'
|
||||
}
|
||||
]
|
||||
|
||||
const udpUseCases = [
|
||||
{
|
||||
icon: '🎮',
|
||||
title: '在线游戏',
|
||||
reason: '速度优先,偶尔丢包可接受'
|
||||
},
|
||||
{
|
||||
icon: '📞',
|
||||
title: '视频通话',
|
||||
reason: '实时性要求高,延迟比质量更重要'
|
||||
},
|
||||
{
|
||||
icon: '📺',
|
||||
title: '直播流',
|
||||
reason: '持续的数据流,丢帧比卡顿好'
|
||||
},
|
||||
{
|
||||
icon: '🔍',
|
||||
title: 'DNS 查询',
|
||||
reason: '请求数据小,快速响应比可靠传输重要'
|
||||
}
|
||||
]
|
||||
|
||||
const commonPorts = [
|
||||
{ number: '80', service: 'HTTP', desc: '网页浏览' },
|
||||
{ number: '443', service: 'HTTPS', desc: '加密网页浏览' },
|
||||
{ number: '22', service: 'SSH', desc: '远程登录' },
|
||||
{ number: '25', service: 'SMTP', desc: '发送邮件' },
|
||||
{ number: '53', service: 'DNS', desc: '域名解析' },
|
||||
{ number: '3306', service: 'MySQL', desc: '数据库连接' }
|
||||
]
|
||||
|
||||
const currentProtocol = computed(() => protocolData[activeProtocol.value])
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.transport-layer-demo {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.demo-header .icon { font-size: 1.5rem; }
|
||||
.demo-header .title { font-weight: 700; font-size: 1.1rem; }
|
||||
.demo-header .subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
|
||||
|
||||
.protocol-tabs {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.tab-btn {
|
||||
flex: 1;
|
||||
padding: 0.75rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.tab-btn:hover {
|
||||
border-color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.tab-btn.active {
|
||||
background: var(--vp-c-brand);
|
||||
border-color: var(--vp-c-brand);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.protocol-visual {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.visual-header {
|
||||
margin-bottom: 1.5rem;
|
||||
padding-bottom: 1rem;
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.protocol-title {
|
||||
display: block;
|
||||
font-weight: 600;
|
||||
font-size: 1.1rem;
|
||||
color: var(--vp-c-brand);
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.protocol-slogan {
|
||||
font-size: 0.9rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.tcp-demo {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.connection-stages {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.stage-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
padding: 0.75rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.stage-item:hover {
|
||||
border-color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.stage-item.active {
|
||||
border-color: var(--vp-c-brand);
|
||||
background: var(--vp-c-brand-soft);
|
||||
}
|
||||
|
||||
.stage-number {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.stage-text {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.tcp-reliability {
|
||||
border-top: 1px solid var(--vp-c-divider);
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
|
||||
.reliability-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.mechanism-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.mechanism-card {
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.mech-icon {
|
||||
font-size: 1.8rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.mech-title {
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.mech-desc {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.udp-demo {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.udp-comparison {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.comparison-side {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.side-header {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.side-animation {
|
||||
min-height: 80px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.packet {
|
||||
padding: 0.5rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 4px;
|
||||
font-size: 0.85rem;
|
||||
animation: slideRight 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.packet.fast {
|
||||
background: rgba(59, 130, 246, 0.1);
|
||||
border-color: #3b82f6;
|
||||
animation: slideRight 0.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes slideRight {
|
||||
0% { transform: translateX(-100%); opacity: 0; }
|
||||
50% { opacity: 1; }
|
||||
100% { transform: translateX(100%); opacity: 0; }
|
||||
}
|
||||
|
||||
.side-desc {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.vs-badge {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
font-weight: 700;
|
||||
font-size: 0.9rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.udp-use-cases {
|
||||
border-top: 1px solid var(--vp-c-divider);
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
|
||||
.use-cases-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.use-cases-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.use-case-card {
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.use-icon {
|
||||
font-size: 1.8rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.use-title {
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.use-reason {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.port-section {
|
||||
border-top: 1px solid var(--vp-c-divider);
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
|
||||
.port-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.port-intro {
|
||||
font-size: 0.9rem;
|
||||
color: var(--vp-c-text-2);
|
||||
line-height: 1.5;
|
||||
margin-bottom: 1rem;
|
||||
padding: 0.75rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.port-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.port-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 0.75rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.port-number {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
border-radius: 6px;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-weight: 700;
|
||||
font-size: 0.9rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.port-service {
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.port-desc {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.udp-comparison {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.vs-badge {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
+530
@@ -0,0 +1,530 @@
|
||||
<template>
|
||||
<div class="tree-structure-demo">
|
||||
<div class="demo-header">
|
||||
<span class="icon">🌳</span>
|
||||
<span class="title">树形结构:层级关系的表示</span>
|
||||
<span class="subtitle">像家谱一样的组织方式</span>
|
||||
</div>
|
||||
|
||||
<div class="tree-selector">
|
||||
<div class="selector-label">选择树的类型:</div>
|
||||
<div class="selector-buttons">
|
||||
<button
|
||||
v-for="type in treeTypes"
|
||||
:key="type.id"
|
||||
:class="['type-btn', { active: activeTreeType === type.id }]"
|
||||
@click="activeTreeType = type.id"
|
||||
>
|
||||
{{ type.icon }} {{ type.name }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 二叉搜索树 -->
|
||||
<div v-if="activeTreeType === 'binary'" class="tree-display">
|
||||
<div class="tree-canvas">
|
||||
<svg viewBox="0 0 600 350" class="tree-svg">
|
||||
<!-- 连接线 -->
|
||||
<line v-for="line in binaryTreeLines" :key="line.id"
|
||||
:x1="line.x1" :y1="line.y1"
|
||||
:x2="line.x2" :y2="line.y2"
|
||||
stroke="var(--vp-c-divider)"
|
||||
stroke-width="2"
|
||||
/>
|
||||
|
||||
<!-- 节点 -->
|
||||
<g v-for="node in binaryTreeNodes" :key="node.id"
|
||||
:class="['tree-node', { root: node.isRoot, leaf: node.isLeaf }]"
|
||||
:style="{ transform: `translate(${node.x}px, ${node.y}px)` }"
|
||||
>
|
||||
<circle cx="0" cy="0" r="25" fill="var(--vp-c-brand-soft)" stroke="var(--vp-c-brand)" stroke-width="2" />
|
||||
<text x="0" y="0" text-anchor="middle" dominant-baseline="middle" fill="var(--vp-c-brand)" font-size="14" font-weight="600">{{ node.value }}</text>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 文件系统树 -->
|
||||
<div v-if="activeTreeType === 'filesystem'" class="filesystem-tree">
|
||||
<div class="fs-root">
|
||||
<div class="fs-node root">📁 根目录 /</div>
|
||||
<div class="fs-children">
|
||||
<div class="fs-branch">
|
||||
<div class="fs-node">📁 home</div>
|
||||
<div class="fs-children">
|
||||
<div class="fs-node">👤 user</div>
|
||||
<div class="fs-children">
|
||||
<div class="fs-node">📄 document.txt</div>
|
||||
<div class="fs-node">🖼️ photo.jpg</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fs-branch">
|
||||
<div class="fs-node">📁 var</div>
|
||||
<div class="fs-children">
|
||||
<div class="fs-node">📁 www</div>
|
||||
<div class="fs-children">
|
||||
<div class="fs-node">📄 index.html</div>
|
||||
<div class="fs-node">📄 style.css</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fs-branch">
|
||||
<div class="fs-node">📁 etc</div>
|
||||
<div class="fs-children">
|
||||
<div class="fs-node">📄 config.conf</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- DOM 树 -->
|
||||
<div v-if="activeTreeType === 'dom'" class="dom-tree">
|
||||
<div class="dom-preview">
|
||||
<div class="preview-title">HTML 结构</div>
|
||||
<div class="preview-html">
|
||||
<html>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>标题</h1>
|
||||
<p>段落</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dom-structure">
|
||||
<div class="structure-title">DOM 树结构</div>
|
||||
<div class="tree-nested">
|
||||
<div class="dom-node root">
|
||||
<span class="node-tag">html</span>
|
||||
<div class="dom-children">
|
||||
<div class="dom-node">
|
||||
<span class="node-tag">body</span>
|
||||
<div class="dom-children">
|
||||
<div class="dom-node">
|
||||
<span class="node-tag">div</span>
|
||||
<span class="node-class">.container</span>
|
||||
<div class="dom-children">
|
||||
<div class="dom-node">
|
||||
<span class="node-tag">h1</span>
|
||||
<span class="node-text">"标题"</span>
|
||||
</div>
|
||||
<div class="dom-node">
|
||||
<span class="node-tag">p</span>
|
||||
<span class="node-text">"段落"</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 树的特点 -->
|
||||
<div class="tree-features">
|
||||
<div class="features-title">树形结构的特点</div>
|
||||
<div class="features-grid">
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">🌲</div>
|
||||
<div class="feature-title">层级关系</div>
|
||||
<div class="feature-desc">节点之间是一对多的父子关系</div>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">🎯</div>
|
||||
<div class="feature-title">单一根节点</div>
|
||||
<div class="feature-desc">除根节点外,每个节点只有一个父节点</div>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">🔍</div>
|
||||
<div class="feature-title">高效查找</div>
|
||||
<div class="feature-desc">二叉搜索树的查找时间是 O(log n)</div>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">🔄</div>
|
||||
<div class="feature-title">多种遍历</div>
|
||||
<div class="feature-desc">前序、中序、后序、层序遍历</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 应用场景 -->
|
||||
<div class="applications">
|
||||
<div class="app-title">应用场景</div>
|
||||
<div class="app-list">
|
||||
<div class="app-item">
|
||||
<span class="app-icon">📁</span>
|
||||
<div class="app-content">
|
||||
<div class="app-name">文件系统</div>
|
||||
<div class="app-desc">文件夹和文件的层级组织</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="app-item">
|
||||
<span class="app-icon">🌐</span>
|
||||
<div class="app-content">
|
||||
<div class="app-name">HTML DOM</div>
|
||||
<div class="app-desc">网页元素的嵌套结构</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="app-item">
|
||||
<span class="app-icon">🏢</span>
|
||||
<div class="app-content">
|
||||
<div class="app-name">组织架构</div>
|
||||
<div class="app-desc">公司的管理层级关系</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="app-item">
|
||||
<span class="app-icon">🌲</span>
|
||||
<div class="app-content">
|
||||
<div class="app-name">决策树</div>
|
||||
<div class="app-desc">机器学习的分类算法</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const activeTreeType = ref('binary')
|
||||
|
||||
const treeTypes = [
|
||||
{ id: 'binary', name: '二叉搜索树', icon: '🌳' },
|
||||
{ id: 'filesystem', name: '文件系统', icon: '📁' },
|
||||
{ id: 'dom', name: 'DOM 树', icon: '🌐' }
|
||||
]
|
||||
|
||||
const binaryTreeNodes = [
|
||||
{ id: 1, value: 50, x: 300, y: 40, isRoot: true },
|
||||
{ id: 2, value: 30, x: 180, y: 120 },
|
||||
{ id: 3, value: 70, x: 420, y: 120 },
|
||||
{ id: 4, value: 20, x: 100, y: 200, isLeaf: true },
|
||||
{ id: 5, value: 40, x: 260, y: 200, isLeaf: true },
|
||||
{ id: 6, value: 60, x: 340, y: 200, isLeaf: true },
|
||||
{ id: 7, value: 80, x: 500, y: 200, isLeaf: true }
|
||||
]
|
||||
|
||||
const binaryTreeLines = [
|
||||
{ id: 1, x1: 300, y1: 65, x2: 180, y2: 95 },
|
||||
{ id: 2, x1: 300, y1: 65, x2: 420, y2: 95 },
|
||||
{ id: 3, x1: 180, y1: 145, x2: 100, y2: 175 },
|
||||
{ id: 4, x1: 180, y1: 145, x2: 260, y2: 175 },
|
||||
{ id: 5, x1: 420, y1: 145, x2: 340, y2: 175 },
|
||||
{ id: 6, x1: 420, y1: 145, x2: 500, y2: 175 }
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tree-structure-demo {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.demo-header .icon { font-size: 1.5rem; }
|
||||
.demo-header .title { font-weight: 700; font-size: 1.1rem; }
|
||||
.demo-header .subtitle { color: var(--vp-c-text-2); font-size: 0.9rem; }
|
||||
|
||||
.tree-selector {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.selector-label {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.selector-buttons {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.type-btn {
|
||||
padding: 0.6rem 1rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 0.9rem;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.type-btn:hover {
|
||||
border-color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.type-btn.active {
|
||||
background: var(--vp-c-brand);
|
||||
border-color: var(--vp-c-brand);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.tree-display {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.tree-canvas {
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.tree-svg {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.tree-node circle {
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.tree-node:hover circle {
|
||||
fill: var(--vp-c-brand);
|
||||
stroke-width: 3;
|
||||
}
|
||||
|
||||
.filesystem-tree {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.fs-root {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.fs-node {
|
||||
padding: 0.5rem 0.75rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 4px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.fs-node.root {
|
||||
background: var(--vp-c-brand-soft);
|
||||
border-color: var(--vp-c-brand);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.fs-children {
|
||||
margin-left: 1.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.dom-tree {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.dom-tree {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.dom-preview {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.preview-title {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.preview-html {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 0.8rem;
|
||||
line-height: 1.8;
|
||||
color: var(--vp-c-text-2);
|
||||
background: var(--vp-c-bg-soft);
|
||||
padding: 1rem;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.dom-structure {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.structure-title {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.tree-nested {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.dom-node {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
align-items: center;
|
||||
padding: 0.5rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.dom-children {
|
||||
margin-left: 1.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.node-tag {
|
||||
padding: 0.25rem 0.5rem;
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
border-radius: 4px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.node-class {
|
||||
padding: 0.25rem 0.5rem;
|
||||
background: #f59e0b;
|
||||
color: white;
|
||||
border-radius: 4px;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.node-text {
|
||||
color: var(--vp-c-text-2);
|
||||
font-size: 0.8rem;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.tree-features {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.features-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.features-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.feature-card {
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 6px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.feature-icon {
|
||||
font-size: 1.8rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.feature-title {
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.feature-desc {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.applications {
|
||||
border-top: 1px solid var(--vp-c-divider);
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
|
||||
.app-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.app-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.app-item {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.app-icon {
|
||||
font-size: 1.5rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.app-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.app-name {
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.app-desc {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
line-height: 1.4;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user