refactor: 重构 api-intro、api-design、transistor-to-cpu 组件为紧凑布局

- 重构 api-intro 7 个 Vue 组件为更紧凑的左右布局
- 重构 api-design 相关组件
- 重构 transistor-to-cpu 相关组件
- 统一使用 demo-root -> demo-header -> demo-layout -> info-box 结构
- 扩写文章内容为 MIT 讲义风格
This commit is contained in:
sanbuphy
2026-02-23 01:50:43 +08:00
parent 2a0fdd3392
commit 1062e2e16f
68 changed files with 4455 additions and 3469 deletions
@@ -7,8 +7,8 @@
<div class="demo-content">
<div class="algorithm-tabs">
<button
v-for="algo in algorithms"
<button
v-for="algo in algorithms"
:key="algo.name"
:class="['tab-btn', { active: activeAlgo === algo.name }]"
@click="activeAlgo = algo.name"
@@ -24,10 +24,7 @@
</div>
<div class="visual-content">
<div
v-if="activeAlgo === '二分查找'"
class="binary-search"
>
<div v-if="activeAlgo === '二分查找'" class="binary-search">
<div class="search-input">
<span>在有序数组中查找</span>
<input
@@ -35,20 +32,15 @@
type="number"
class="num-input"
placeholder="输入数字"
>
<button
class="search-btn"
@click="runBinarySearch"
>
查找
</button>
/>
<button class="search-btn" @click="runBinarySearch">查找</button>
</div>
<div class="array-display">
<div
v-for="(num, i) in sortedArray"
<div
v-for="(num, i) in sortedArray"
:key="i"
class="array-cell"
:class="{
:class="{
highlight: i >= searchRange.left && i <= searchRange.right,
found: i === foundIndex,
mid: i === midIndex
@@ -57,44 +49,27 @@
{{ num }}
</div>
</div>
<div
v-if="searchSteps.length"
class="search-info"
>
<div
v-for="(step, i) in searchSteps"
:key="i"
class="step"
>
<div v-if="searchSteps.length" class="search-info">
<div v-for="(step, i) in searchSteps" :key="i" class="step">
{{ step }}
</div>
</div>
</div>
<div
v-else-if="activeAlgo === '排序'"
class="sorting"
>
<div v-else-if="activeAlgo === '排序'" class="sorting">
<div class="sort-controls">
<button
class="sort-btn"
@click="resetArray"
>
重置数组
</button>
<button
class="sort-btn"
@click="runSort"
>
开始排序
</button>
<button class="sort-btn" @click="resetArray">重置数组</button>
<button class="sort-btn" @click="runSort">开始排序</button>
</div>
<div class="array-display">
<div
v-for="(num, i) in sortArray"
<div
v-for="(num, i) in sortArray"
:key="i"
class="array-cell"
:class="{ comparing: comparingIndices.includes(i), sorted: sortedIndices.includes(i) }"
:class="{
comparing: comparingIndices.includes(i),
sorted: sortedIndices.includes(i)
}"
>
{{ num }}
</div>
@@ -104,10 +79,7 @@
</div>
</div>
<div
v-else-if="activeAlgo === '递归'"
class="recursion"
>
<div v-else-if="activeAlgo === '递归'" class="recursion">
<div class="recursion-input">
<span>计算斐波那契数列第</span>
<input
@@ -116,28 +88,15 @@
min="1"
max="15"
class="num-input"
>
/>
<span></span>
<button
class="calc-btn"
@click="calcFib"
>
计算
</button>
<button class="calc-btn" @click="calcFib">计算</button>
</div>
<div
v-if="fibResult !== null"
class="fib-result"
>
<div v-if="fibResult !== null" class="fib-result">
<span class="result-value">F({{ fibN }}) = {{ fibResult }}</span>
</div>
<div
v-if="fibSteps.length"
class="recursion-tree"
>
<div class="tree-title">
递归调用过程
</div>
<div v-if="fibSteps.length" class="recursion-tree">
<div class="tree-title">递归调用过程</div>
<div class="tree-content">
<div
v-for="(step, i) in fibSteps.slice(0, 8)"
@@ -146,20 +105,14 @@
>
{{ step }}
</div>
<div
v-if="fibSteps.length > 8"
class="tree-more"
>
<div v-if="fibSteps.length > 8" class="tree-more">
... {{ fibSteps.length }} 次调用
</div>
</div>
</div>
</div>
<div
v-else-if="activeAlgo === '贪心'"
class="greedy"
>
<div v-else-if="activeAlgo === '贪心'" class="greedy">
<div class="greedy-desc">
硬币找零问题用最少的硬币凑出指定金额
</div>
@@ -170,30 +123,18 @@
type="number"
min="1"
class="num-input"
>
<button
class="calc-btn"
@click="calcCoins"
>
计算
</button>
/>
<button class="calc-btn" @click="calcCoins">计算</button>
</div>
<div class="coins-available">
可用硬币{{ coins.join(', ') }}
</div>
<div
v-if="coinResult.length"
class="coin-result"
>
<div class="result-title">
找零方案
</div>
<div v-if="coinResult.length" class="coin-result">
<div class="result-title">找零方案</div>
<div class="coin-list">
<span
v-for="(c, i) in coinResult"
:key="i"
class="coin"
>{{ c }}</span>
<span v-for="(c, i) in coinResult" :key="i" class="coin"
>{{ c }}</span
>
</div>
<div class="result-summary">
{{ coinResult.length }} 枚硬币
@@ -204,20 +145,11 @@
</div>
<div class="complexity-info">
<div class="info-title">
时间复杂度速查
</div>
<div class="info-title">时间复杂度速查</div>
<div class="complexity-list">
<div
v-for="c in complexities"
:key="c.name"
class="complexity-item"
>
<div v-for="c in complexities" :key="c.name" class="complexity-item">
<span class="c-name">{{ c.name }}</span>
<span
class="c-value"
:class="c.class"
>{{ c.value }}</span>
<span class="c-value" :class="c.class">{{ c.value }}</span>
<span class="c-desc">{{ c.desc }}</span>
</div>
</div>
@@ -225,7 +157,8 @@
</div>
<div class="info-box">
<strong>核心思想</strong>算法是解决问题的方法好的算法能让程序效率提升几个数量级理解算法思维比记住具体算法更重要
<strong>核心思想</strong
>算法是解决问题的方法好的算法能让程序效率提升几个数量级理解算法思维比记住具体算法更重要
</div>
</div>
</template>
@@ -243,7 +176,7 @@ const algorithms = [
]
const currentAlgo = computed(() => {
return algorithms.find(a => a.name === activeAlgo.value)
return algorithms.find((a) => a.name === activeAlgo.value)
})
const sortedArray = ref([1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25])
@@ -259,25 +192,31 @@ const runBinarySearch = () => {
midIndex.value = -1
let left = 0
let right = sortedArray.value.length - 1
while (left <= right) {
const mid = Math.floor((left + right) / 2)
midIndex.value = mid
searchRange.left = left
searchRange.right = right
searchSteps.value.push(`查找范围 [${left}, ${right}],中间位置 ${mid},值 ${sortedArray.value[mid]}`)
searchSteps.value.push(
`查找范围 [${left}, ${right}],中间位置 ${mid},值 ${sortedArray.value[mid]}`
)
if (sortedArray.value[mid] === searchTarget.value) {
foundIndex.value = mid
searchSteps.value.push(`找到目标 ${searchTarget.value} 在位置 ${mid}`)
return
} else if (sortedArray.value[mid] < searchTarget.value) {
left = mid + 1
searchSteps.value.push(`${sortedArray.value[mid]} < ${searchTarget.value},在右半部分继续查找`)
searchSteps.value.push(
`${sortedArray.value[mid]} < ${searchTarget.value},在右半部分继续查找`
)
} else {
right = mid - 1
searchSteps.value.push(`${sortedArray.value[mid]} > ${searchTarget.value},在左半部分继续查找`)
searchSteps.value.push(
`${sortedArray.value[mid]} > ${searchTarget.value},在左半部分继续查找`
)
}
}
searchSteps.value.push(`未找到目标 ${searchTarget.value}`)
@@ -299,18 +238,18 @@ const runSort = async () => {
sortedIndices.value = []
const arr = [...sortArray.value]
const n = arr.length
for (let i = 0; i < n - 1; i++) {
for (let j = 0; j < n - i - 1; j++) {
comparingIndices.value = [j, j + 1]
sortStatus.value = `比较 ${arr[j]}${arr[j + 1]}`
await new Promise(r => setTimeout(r, 300))
await new Promise((r) => setTimeout(r, 300))
if (arr[j] > arr[j + 1]) {
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]
;[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]
sortArray.value = [...arr]
sortStatus.value = `交换 ${arr[j + 1]}${arr[j]}`
await new Promise(r => setTimeout(r, 200))
await new Promise((r) => setTimeout(r, 200))
}
}
sortedIndices.value.push(n - i - 1)
@@ -353,7 +292,12 @@ const complexities = [
{ name: 'O(1)', value: '常数', desc: '最优,如数组访问', class: 'good' },
{ name: 'O(log n)', value: '对数', desc: '很好,如二分查找', class: 'good' },
{ name: 'O(n)', value: '线性', desc: '一般,如遍历', class: 'mid' },
{ name: 'O(n log n)', value: '线性对数', desc: '可接受,如快速排序', class: 'mid' },
{
name: 'O(n log n)',
value: '线性对数',
desc: '可接受,如快速排序',
class: 'mid'
},
{ name: 'O(n²)', value: '平方', desc: '较慢,如冒泡排序', class: 'bad' },
{ name: 'O(2ⁿ)', value: '指数', desc: '很慢,如暴力递归', class: 'bad' }
]
@@ -375,8 +319,15 @@ const complexities = [
margin-bottom: 0.75rem;
}
.demo-header .title { font-weight: bold; font-size: 1rem; }
.demo-header .subtitle { color: var(--vp-c-text-2); font-size: 0.85rem; margin-left: 0.5rem; }
.demo-header .title {
font-weight: bold;
font-size: 1rem;
}
.demo-header .subtitle {
color: var(--vp-c-text-2);
font-size: 0.85rem;
margin-left: 0.5rem;
}
.algorithm-tabs {
display: flex;
@@ -422,7 +373,9 @@ const complexities = [
margin-left: 0.5rem;
}
.search-input, .greedy-input, .recursion-input {
.search-input,
.greedy-input,
.recursion-input {
display: flex;
align-items: center;
gap: 0.5rem;
@@ -438,7 +391,9 @@ const complexities = [
background: var(--vp-c-bg-alt);
}
.search-btn, .sort-btn, .calc-btn {
.search-btn,
.sort-btn,
.calc-btn {
padding: 0.25rem 0.75rem;
background: var(--vp-c-brand);
color: white;
@@ -487,7 +442,8 @@ const complexities = [
background: var(--vp-c-success-soft);
}
.search-info, .sort-info {
.search-info,
.sort-info {
font-size: 0.8rem;
color: var(--vp-c-text-2);
}
@@ -614,9 +570,15 @@ const complexities = [
min-width: 50px;
}
.c-value.good { color: var(--vp-c-success); }
.c-value.mid { color: var(--vp-c-warning); }
.c-value.bad { color: var(--vp-c-danger); }
.c-value.good {
color: var(--vp-c-success);
}
.c-value.mid {
color: var(--vp-c-warning);
}
.c-value.bad {
color: var(--vp-c-danger);
}
.c-desc {
color: var(--vp-c-text-2);
@@ -632,5 +594,4 @@ const complexities = [
display: flex;
gap: 0.25rem;
}
</style>