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:
@@ -1,16 +1,30 @@
|
||||
<template>
|
||||
<div class="adder-demo">
|
||||
<div class="demo-label">二进制加法器 ── 输入 0–15 的两个数,观察逐位计算过程</div>
|
||||
<div class="demo-label">
|
||||
二进制加法器 ── 输入 0–15 的两个数,观察逐位计算过程
|
||||
</div>
|
||||
|
||||
<div class="control-row">
|
||||
<label class="input-group">
|
||||
<span class="input-label">A</span>
|
||||
<input v-model.number="inputA" type="number" min="0" max="15" class="num-input" />
|
||||
<input
|
||||
v-model.number="inputA"
|
||||
type="number"
|
||||
min="0"
|
||||
max="15"
|
||||
class="num-input"
|
||||
/>
|
||||
</label>
|
||||
<span class="op-sign">+</span>
|
||||
<label class="input-group">
|
||||
<span class="input-label">B</span>
|
||||
<input v-model.number="inputB" type="number" min="0" max="15" class="num-input" />
|
||||
<input
|
||||
v-model.number="inputB"
|
||||
type="number"
|
||||
min="0"
|
||||
max="15"
|
||||
class="num-input"
|
||||
/>
|
||||
</label>
|
||||
<span class="op-sign">=</span>
|
||||
<span class="result-num">{{ resultDec }}</span>
|
||||
@@ -20,21 +34,39 @@
|
||||
<div class="binary-row">
|
||||
<span class="binary-label">A</span>
|
||||
<span class="binary-bits">
|
||||
<span v-for="(b, i) in bitsA" :key="'a'+i" class="bit" :class="{ hl: activeBit === (3 - i) }">{{ b }}</span>
|
||||
<span
|
||||
v-for="(b, i) in bitsA"
|
||||
:key="'a' + i"
|
||||
class="bit"
|
||||
:class="{ hl: activeBit === 3 - i }"
|
||||
>{{ b }}</span
|
||||
>
|
||||
</span>
|
||||
<span class="binary-dec">= {{ clampedA }}</span>
|
||||
</div>
|
||||
<div class="binary-row">
|
||||
<span class="binary-label">B</span>
|
||||
<span class="binary-bits">
|
||||
<span v-for="(b, i) in bitsB" :key="'b'+i" class="bit" :class="{ hl: activeBit === (3 - i) }">{{ b }}</span>
|
||||
<span
|
||||
v-for="(b, i) in bitsB"
|
||||
:key="'b' + i"
|
||||
class="bit"
|
||||
:class="{ hl: activeBit === 3 - i }"
|
||||
>{{ b }}</span
|
||||
>
|
||||
</span>
|
||||
<span class="binary-dec">= {{ clampedB }}</span>
|
||||
</div>
|
||||
<div class="binary-row sum-row">
|
||||
<span class="binary-label">结果</span>
|
||||
<span class="binary-bits">
|
||||
<span v-for="(b, i) in bitsSum" :key="'s'+i" class="bit" :class="{ hl: activeBit === (3 - i) }">{{ b }}</span>
|
||||
<span
|
||||
v-for="(b, i) in bitsSum"
|
||||
:key="'s' + i"
|
||||
class="bit"
|
||||
:class="{ hl: activeBit === 3 - i }"
|
||||
>{{ b }}</span
|
||||
>
|
||||
</span>
|
||||
<span class="binary-dec">= {{ fourBitResult }}</span>
|
||||
</div>
|
||||
@@ -45,7 +77,8 @@
|
||||
|
||||
<div class="stages-row">
|
||||
<div
|
||||
v-for="(stage, idx) in stages" :key="idx"
|
||||
v-for="(stage, idx) in stages"
|
||||
:key="idx"
|
||||
class="stage-card"
|
||||
:class="{ active: activeBit === stage.bitPos }"
|
||||
@mouseenter="activeBit = stage.bitPos"
|
||||
@@ -53,24 +86,40 @@
|
||||
>
|
||||
<div class="stage-head">
|
||||
<span class="stage-pos">第{{ stage.bitPos }}位</span>
|
||||
<span class="stage-type" :class="stage.carryIn !== null ? 'full' : 'half'">
|
||||
<span
|
||||
class="stage-type"
|
||||
:class="stage.carryIn !== null ? 'full' : 'half'"
|
||||
>
|
||||
{{ stage.carryIn !== null ? '全加器' : '半加器' }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="stage-io">
|
||||
<span class="io-item"><span class="io-tag a">A</span>{{ stage.a }}</span>
|
||||
<span class="io-item"><span class="io-tag b">B</span>{{ stage.b }}</span>
|
||||
<span v-if="stage.carryIn !== null" class="io-item"><span class="io-tag cin">Cin</span>{{ stage.carryIn }}</span>
|
||||
<span class="io-item"
|
||||
><span class="io-tag a">A</span>{{ stage.a }}</span
|
||||
>
|
||||
<span class="io-item"
|
||||
><span class="io-tag b">B</span>{{ stage.b }}</span
|
||||
>
|
||||
<span v-if="stage.carryIn !== null" class="io-item"
|
||||
><span class="io-tag cin">Cin</span>{{ stage.carryIn }}</span
|
||||
>
|
||||
</div>
|
||||
<div class="stage-divider"></div>
|
||||
<div class="stage-io">
|
||||
<span class="io-item"><span class="io-tag s">S</span><strong>{{ stage.sum }}</strong></span>
|
||||
<span class="io-item"><span class="io-tag cout">C</span>{{ stage.carryOut }}</span>
|
||||
<span class="io-item"
|
||||
><span class="io-tag s">S</span
|
||||
><strong>{{ stage.sum }}</strong></span
|
||||
>
|
||||
<span class="io-item"
|
||||
><span class="io-tag cout">C</span>{{ stage.carryOut }}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="demo-caption">鼠标悬停某一位,查看该位加法器的输入 / 输出 · 就像手算竖式"逢二进一"</div>
|
||||
<div class="demo-caption">
|
||||
鼠标悬停某一位,查看该位加法器的输入 / 输出 · 就像手算竖式"逢二进一"
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -90,8 +139,12 @@ function clamp(n) {
|
||||
const clampedA = computed(() => clamp(inputA.value))
|
||||
const clampedB = computed(() => clamp(inputB.value))
|
||||
|
||||
const bitsA = computed(() => (clampedA.value >>> 0).toString(2).padStart(4, '0').split(''))
|
||||
const bitsB = computed(() => (clampedB.value >>> 0).toString(2).padStart(4, '0').split(''))
|
||||
const bitsA = computed(() =>
|
||||
(clampedA.value >>> 0).toString(2).padStart(4, '0').split('')
|
||||
)
|
||||
const bitsB = computed(() =>
|
||||
(clampedB.value >>> 0).toString(2).padStart(4, '0').split('')
|
||||
)
|
||||
|
||||
const stages = computed(() => {
|
||||
const A = clampedA.value
|
||||
@@ -106,10 +159,17 @@ const stages = computed(() => {
|
||||
sum = a ^ b
|
||||
carryOut = a & b
|
||||
} else {
|
||||
sum = (a ^ b) ^ carryIn
|
||||
sum = a ^ b ^ carryIn
|
||||
carryOut = (a & b) | (carryIn & (a ^ b))
|
||||
}
|
||||
result.push({ bitPos: i, a, b, carryIn: carryIn === null ? null : carryIn, sum, carryOut })
|
||||
result.push({
|
||||
bitPos: i,
|
||||
a,
|
||||
b,
|
||||
carryIn: carryIn === null ? null : carryIn,
|
||||
sum,
|
||||
carryOut
|
||||
})
|
||||
carryIn = carryOut
|
||||
}
|
||||
return result
|
||||
@@ -126,7 +186,9 @@ const fourBitResult = computed(() =>
|
||||
|
||||
const overflow = computed(() => clampedA.value + clampedB.value > 15)
|
||||
const resultDec = computed(() =>
|
||||
overflow.value ? `${fourBitResult.value}(溢出)` : String(fourBitResult.value)
|
||||
overflow.value
|
||||
? `${fourBitResult.value}(溢出)`
|
||||
: String(fourBitResult.value)
|
||||
)
|
||||
</script>
|
||||
|
||||
@@ -334,11 +396,21 @@ const resultDec = computed(() =>
|
||||
font-family: system-ui;
|
||||
}
|
||||
|
||||
.io-tag.a { background: var(--vp-c-brand-1); }
|
||||
.io-tag.b { background: #8b5cf6; }
|
||||
.io-tag.cin { background: #d97706; }
|
||||
.io-tag.s { background: var(--vp-c-green-1, #16a34a); }
|
||||
.io-tag.cout { background: #d97706; }
|
||||
.io-tag.a {
|
||||
background: var(--vp-c-brand-1);
|
||||
}
|
||||
.io-tag.b {
|
||||
background: #8b5cf6;
|
||||
}
|
||||
.io-tag.cin {
|
||||
background: #d97706;
|
||||
}
|
||||
.io-tag.s {
|
||||
background: var(--vp-c-green-1, #16a34a);
|
||||
}
|
||||
.io-tag.cout {
|
||||
background: #d97706;
|
||||
}
|
||||
|
||||
.stage-divider {
|
||||
height: 1px;
|
||||
|
||||
+89
-128
@@ -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>
|
||||
|
||||
+41
-16
@@ -9,9 +9,9 @@
|
||||
<div class="analogy-content">
|
||||
<div class="analogy-icon">📖</div>
|
||||
<div class="analogy-text">
|
||||
<strong>算法就像菜谱:</strong><br>
|
||||
食材 = 数据<br>
|
||||
烹饪步骤 = 算法<br>
|
||||
<strong>算法就像菜谱:</strong><br />
|
||||
食材 = 数据<br />
|
||||
烹饪步骤 = 算法<br />
|
||||
美味菜肴 = 结果
|
||||
</div>
|
||||
</div>
|
||||
@@ -49,8 +49,12 @@
|
||||
<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 class="analogy-scenario">
|
||||
{{ currentCategory.analogy.scenario }}
|
||||
</div>
|
||||
<div class="analogy-explanation">
|
||||
{{ currentCategory.analogy.explanation }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -58,7 +62,9 @@
|
||||
<div class="section-title">时间复杂度</div>
|
||||
<div class="complexity-display">
|
||||
<div class="complexity-bigO">{{ currentCategory.complexity }}</div>
|
||||
<div class="complexity-desc">{{ currentCategory.complexityDesc }}</div>
|
||||
<div class="complexity-desc">
|
||||
{{ currentCategory.complexityDesc }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -140,7 +146,8 @@ const categories = [
|
||||
idea: '从数据集合中找到特定元素的过程',
|
||||
analogy: {
|
||||
scenario: '在字典里查单词',
|
||||
explanation: '顺序查找 = 从第一页翻到最后一页;二分查找 = 直接翻到中间,判断在前半还是后半'
|
||||
explanation:
|
||||
'顺序查找 = 从第一页翻到最后一页;二分查找 = 直接翻到中间,判断在前半还是后半'
|
||||
},
|
||||
complexity: 'O(log n)',
|
||||
complexityDesc: '二分查找非常快,每次排除一半数据',
|
||||
@@ -154,7 +161,8 @@ const categories = [
|
||||
idea: '将无序数据重新排列成有序序列',
|
||||
analogy: {
|
||||
scenario: '整理扑克牌',
|
||||
explanation: '插入排序 = 每次拿一张牌插到正确的位置;快速排序 = 把牌分成大小两堆,递归整理'
|
||||
explanation:
|
||||
'插入排序 = 每次拿一张牌插到正确的位置;快速排序 = 把牌分成大小两堆,递归整理'
|
||||
},
|
||||
complexity: 'O(n log n)',
|
||||
complexityDesc: '快速排序、归并排序是最高效的通用排序算法',
|
||||
@@ -168,7 +176,8 @@ const categories = [
|
||||
idea: '将大问题分解为相同类型的小问题',
|
||||
analogy: {
|
||||
scenario: '俄罗斯套娃',
|
||||
explanation: '打开一个大娃娃,里面有个小一点的娃娃,再打开还有更小的...直到最小的一个'
|
||||
explanation:
|
||||
'打开一个大娃娃,里面有个小一点的娃娃,再打开还有更小的...直到最小的一个'
|
||||
},
|
||||
complexity: 'O(log n) 到 O(2ⁿ)',
|
||||
complexityDesc: '取决于问题类型,二分查找递归很快,斐波那契递归较慢',
|
||||
@@ -182,7 +191,8 @@ const categories = [
|
||||
idea: '在每一步选择中都采取当前状态下最优的选择',
|
||||
analogy: {
|
||||
scenario: '找零钱',
|
||||
explanation: '找 37 元零钱:先拿一张 20(最大可能),再拿 10、5、1、1,每次都选最大的面值'
|
||||
explanation:
|
||||
'找 37 元零钱:先拿一张 20(最大可能),再拿 10、5、1、1,每次都选最大的面值'
|
||||
},
|
||||
complexity: 'O(n) 或 O(n log n)',
|
||||
complexityDesc: '通常很快,但可能得不到全局最优解',
|
||||
@@ -196,7 +206,8 @@ const categories = [
|
||||
idea: '将复杂问题分解为子问题,保存子问题的解',
|
||||
analogy: {
|
||||
scenario: '爬楼梯',
|
||||
explanation: '要爬到第 n 级,可以从 n-1 级跨 1 步,或从 n-2 级跨 2 步,记住之前的结果避免重复计算'
|
||||
explanation:
|
||||
'要爬到第 n 级,可以从 n-1 级跨 1 步,或从 n-2 级跨 2 步,记住之前的结果避免重复计算'
|
||||
},
|
||||
complexity: 'O(n²) 或 O(n³)',
|
||||
complexityDesc: '用空间换时间,比递归快很多',
|
||||
@@ -206,12 +217,19 @@ const categories = [
|
||||
|
||||
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 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))
|
||||
const currentCategory = computed(() =>
|
||||
categories.find((c) => c.id === activeCategory.value)
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -230,8 +248,14 @@ const currentCategory = computed(() => categories.find(c => c.id === activeCateg
|
||||
margin-bottom: 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; }
|
||||
.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);
|
||||
@@ -344,7 +368,8 @@ const currentCategory = computed(() => categories.find(c => c.id === activeCateg
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.detail-section {}
|
||||
.detail-section {
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-weight: 600;
|
||||
|
||||
+16
-5
@@ -66,7 +66,9 @@
|
||||
<div class="section-title">时间复杂度</div>
|
||||
<div class="complexity-box">
|
||||
<div class="complexity-value">{{ currentParadigm.complexity }}</div>
|
||||
<div class="complexity-note">{{ currentParadigm.complexityNote }}</div>
|
||||
<div class="complexity-note">
|
||||
{{ currentParadigm.complexityNote }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -216,7 +218,9 @@ const comparisonData = [
|
||||
}
|
||||
]
|
||||
|
||||
const currentParadigm = computed(() => paradigms.find(p => p.id === activeParadigm.value))
|
||||
const currentParadigm = computed(() =>
|
||||
paradigms.find((p) => p.id === activeParadigm.value)
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -235,8 +239,14 @@ const currentParadigm = computed(() => paradigms.find(p => p.id === activeParadi
|
||||
margin-bottom: 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; }
|
||||
.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;
|
||||
@@ -324,7 +334,8 @@ const currentParadigm = computed(() => paradigms.find(p => p.id === activeParadi
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.detail-section {}
|
||||
.detail-section {
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-weight: 600;
|
||||
|
||||
+18
-4
@@ -34,7 +34,11 @@
|
||||
<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">
|
||||
<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>
|
||||
@@ -44,7 +48,11 @@
|
||||
<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">
|
||||
<div
|
||||
v-for="(app, index) in currentProtocol.apps"
|
||||
:key="index"
|
||||
class="app-tag"
|
||||
>
|
||||
{{ app.icon }} {{ app.name }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -268,8 +276,14 @@ const currentProtocol = computed(() => protocolDetails[activeProtocol.value])
|
||||
margin-bottom: 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; }
|
||||
.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;
|
||||
|
||||
+12
-14
@@ -7,8 +7,8 @@
|
||||
|
||||
<div class="code-input">
|
||||
<div class="input-title">输入代码</div>
|
||||
<textarea
|
||||
v-model="sourceCode"
|
||||
<textarea
|
||||
v-model="sourceCode"
|
||||
class="code-textarea"
|
||||
placeholder="输入 C 语言代码..."
|
||||
></textarea>
|
||||
@@ -17,11 +17,7 @@
|
||||
<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 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>
|
||||
@@ -35,11 +31,7 @@
|
||||
<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 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>
|
||||
@@ -147,8 +139,14 @@ const outputFiles = [
|
||||
margin-bottom: 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; }
|
||||
.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;
|
||||
|
||||
+13
-11
@@ -28,7 +28,9 @@
|
||||
<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 v-if="index < translationSteps.length - 1" class="step-arrow">
|
||||
→
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -42,11 +44,7 @@
|
||||
</div>
|
||||
<div class="token-arrow">↓</div>
|
||||
<div class="tokens-list">
|
||||
<div
|
||||
v-for="(token, index) in tokens"
|
||||
:key="index"
|
||||
class="token-item"
|
||||
>
|
||||
<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>
|
||||
@@ -127,9 +125,7 @@
|
||||
<div class="opt-code">x = 10</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="opt-note">
|
||||
编译器会自动优化代码,提高运行效率
|
||||
</div>
|
||||
<div class="opt-note">编译器会自动优化代码,提高运行效率</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -196,8 +192,14 @@ const tokens = [
|
||||
margin-bottom: 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; }
|
||||
.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;
|
||||
|
||||
+9
-4
@@ -105,8 +105,7 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
</script>
|
||||
<script setup></script>
|
||||
|
||||
<style scoped>
|
||||
.data-encoding-basics-demo {
|
||||
@@ -124,8 +123,14 @@
|
||||
margin-bottom: 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; }
|
||||
.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;
|
||||
|
||||
+28
-9
@@ -22,7 +22,11 @@
|
||||
<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">
|
||||
<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>
|
||||
@@ -44,7 +48,11 @@
|
||||
<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
|
||||
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>
|
||||
@@ -67,7 +75,8 @@ const stages = [
|
||||
name: '数据输入',
|
||||
icon: '⌨️',
|
||||
title: '阶段 1:数据输入',
|
||||
description: '用户通过各种输入设备(键盘、鼠标、触摸屏、麦克风等)将信息输入到计算机系统中。',
|
||||
description:
|
||||
'用户通过各种输入设备(键盘、鼠标、触摸屏、麦克风等)将信息输入到计算机系统中。',
|
||||
example: {
|
||||
label: '用户输入文字',
|
||||
items: [
|
||||
@@ -83,7 +92,8 @@ const stages = [
|
||||
name: '数据处理',
|
||||
icon: '🔄',
|
||||
title: '阶段 2:数据处理',
|
||||
description: 'CPU 对输入的数据进行计算、转换、格式化等操作,应用程序根据业务逻辑处理数据。',
|
||||
description:
|
||||
'CPU 对输入的数据进行计算、转换、格式化等操作,应用程序根据业务逻辑处理数据。',
|
||||
example: {
|
||||
label: '文本编辑器处理',
|
||||
items: [
|
||||
@@ -99,7 +109,8 @@ const stages = [
|
||||
name: '数据存储',
|
||||
icon: '💾',
|
||||
title: '阶段 3:数据存储',
|
||||
description: '处理后的数据被保存到存储设备中(内存、硬盘、SSD、云存储等),以便后续使用。',
|
||||
description:
|
||||
'处理后的数据被保存到存储设备中(内存、硬盘、SSD、云存储等),以便后续使用。',
|
||||
example: {
|
||||
label: '保存文档',
|
||||
items: [
|
||||
@@ -115,7 +126,8 @@ const stages = [
|
||||
name: '数据传输',
|
||||
icon: '📡',
|
||||
title: '阶段 4:数据传输',
|
||||
description: '数据通过网络(局域网、互联网)或内部总线从一个位置传输到另一个位置。',
|
||||
description:
|
||||
'数据通过网络(局域网、互联网)或内部总线从一个位置传输到另一个位置。',
|
||||
example: {
|
||||
label: '上传文件',
|
||||
items: [
|
||||
@@ -131,7 +143,8 @@ const stages = [
|
||||
name: '数据输出',
|
||||
icon: '🖥️',
|
||||
title: '阶段 5:数据输出',
|
||||
description: '数据通过输出设备(显示器、打印机、扬声器等)呈现给用户,或传输给其他系统。',
|
||||
description:
|
||||
'数据通过输出设备(显示器、打印机、扬声器等)呈现给用户,或传输给其他系统。',
|
||||
example: {
|
||||
label: '显示网页',
|
||||
items: [
|
||||
@@ -184,8 +197,14 @@ const keyPoints = [
|
||||
margin-bottom: 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; }
|
||||
.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;
|
||||
|
||||
+22
-5
@@ -11,13 +11,22 @@
|
||||
<div
|
||||
v-for="device in devices"
|
||||
:key="device.id"
|
||||
:class="['lan-device', { active: activeDevice === device.id, sender: device.role === 'sender', receiver: device.role === 'receiver' }]"
|
||||
: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 v-if="device.role" class="device-role">
|
||||
{{ device.roleText }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -77,7 +86,9 @@
|
||||
<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>
|
||||
<span class="answer-text"
|
||||
>我是!我的 MAC 地址是 00:11:22:33:44:66</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -194,8 +205,14 @@ const transferSteps = [
|
||||
margin-bottom: 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; }
|
||||
.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);
|
||||
|
||||
+70
-156
@@ -7,8 +7,8 @@
|
||||
|
||||
<div class="demo-content">
|
||||
<div class="structure-tabs">
|
||||
<button
|
||||
v-for="s in structures"
|
||||
<button
|
||||
v-for="s in structures"
|
||||
:key="s.name"
|
||||
:class="['tab-btn', { active: activeStructure === s.name }]"
|
||||
@click="activeStructure = s.name"
|
||||
@@ -24,16 +24,9 @@
|
||||
</div>
|
||||
|
||||
<div class="visual-content">
|
||||
<div
|
||||
v-if="activeStructure === '数组'"
|
||||
class="array-visual"
|
||||
>
|
||||
<div v-if="activeStructure === '数组'" class="array-visual">
|
||||
<div class="array-container">
|
||||
<div
|
||||
v-for="(item, i) in arrayData"
|
||||
:key="i"
|
||||
class="array-item"
|
||||
>
|
||||
<div v-for="(item, i) in arrayData" :key="i" class="array-item">
|
||||
<span class="index">{{ i }}</span>
|
||||
<span class="value">{{ item }}</span>
|
||||
</div>
|
||||
@@ -43,21 +36,13 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else-if="activeStructure === '链表'"
|
||||
class="linked-visual"
|
||||
>
|
||||
<div v-else-if="activeStructure === '链表'" class="linked-visual">
|
||||
<div class="linked-container">
|
||||
<div
|
||||
v-for="(item, i) in linkedData"
|
||||
:key="i"
|
||||
class="linked-node"
|
||||
>
|
||||
<div v-for="(item, i) in linkedData" :key="i" class="linked-node">
|
||||
<span class="node-value">{{ item.value }}</span>
|
||||
<span
|
||||
v-if="i < linkedData.length - 1"
|
||||
class="node-arrow"
|
||||
>→</span>
|
||||
<span v-if="i < linkedData.length - 1" class="node-arrow"
|
||||
>→</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="operation-hint">
|
||||
@@ -65,104 +50,53 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else-if="activeStructure === '栈'"
|
||||
class="stack-visual"
|
||||
>
|
||||
<div v-else-if="activeStructure === '栈'" class="stack-visual">
|
||||
<div class="stack-container">
|
||||
<div
|
||||
v-for="(item, i) in stackData"
|
||||
:key="i"
|
||||
class="stack-item"
|
||||
>
|
||||
<div v-for="(item, i) in stackData" :key="i" class="stack-item">
|
||||
{{ item }}
|
||||
</div>
|
||||
<div class="stack-bottom">
|
||||
栈底
|
||||
</div>
|
||||
<div class="stack-bottom">栈底</div>
|
||||
</div>
|
||||
<div class="stack-ops">
|
||||
<button
|
||||
class="op-btn"
|
||||
@click="pushStack"
|
||||
>
|
||||
入栈 Push
|
||||
</button>
|
||||
<button
|
||||
class="op-btn"
|
||||
@click="popStack"
|
||||
>
|
||||
出栈 Pop
|
||||
</button>
|
||||
</div>
|
||||
<div class="operation-hint">
|
||||
后进先出 (LIFO),操作都是 O(1)
|
||||
<button class="op-btn" @click="pushStack">入栈 Push</button>
|
||||
<button class="op-btn" @click="popStack">出栈 Pop</button>
|
||||
</div>
|
||||
<div class="operation-hint">后进先出 (LIFO),操作都是 O(1)</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else-if="activeStructure === '队列'"
|
||||
class="queue-visual"
|
||||
>
|
||||
<div v-else-if="activeStructure === '队列'" class="queue-visual">
|
||||
<div class="queue-container">
|
||||
<span class="queue-label">出 ←</span>
|
||||
<div
|
||||
v-for="(item, i) in queueData"
|
||||
:key="i"
|
||||
class="queue-item"
|
||||
>
|
||||
<div v-for="(item, i) in queueData" :key="i" class="queue-item">
|
||||
{{ item }}
|
||||
</div>
|
||||
<span class="queue-label">← 入</span>
|
||||
</div>
|
||||
<div class="queue-ops">
|
||||
<button
|
||||
class="op-btn"
|
||||
@click="enqueue"
|
||||
>
|
||||
入队
|
||||
</button>
|
||||
<button
|
||||
class="op-btn"
|
||||
@click="dequeue"
|
||||
>
|
||||
出队
|
||||
</button>
|
||||
</div>
|
||||
<div class="operation-hint">
|
||||
先进先出 (FIFO),操作都是 O(1)
|
||||
<button class="op-btn" @click="enqueue">入队</button>
|
||||
<button class="op-btn" @click="dequeue">出队</button>
|
||||
</div>
|
||||
<div class="operation-hint">先进先出 (FIFO),操作都是 O(1)</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else-if="activeStructure === '哈希表'"
|
||||
class="hash-visual"
|
||||
>
|
||||
<div v-else-if="activeStructure === '哈希表'" class="hash-visual">
|
||||
<div class="hash-container">
|
||||
<div
|
||||
v-for="(bucket, i) in hashData"
|
||||
:key="i"
|
||||
class="hash-bucket"
|
||||
>
|
||||
<div v-for="(bucket, i) in hashData" :key="i" class="hash-bucket">
|
||||
<span class="bucket-index">{{ i }}</span>
|
||||
<div class="bucket-items">
|
||||
<span
|
||||
v-for="(item, j) in bucket"
|
||||
:key="j"
|
||||
class="bucket-item"
|
||||
>{{ item }}</span>
|
||||
>{{ item }}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="operation-hint">
|
||||
查找/插入/删除平均 O(1),最坏 O(n)
|
||||
</div>
|
||||
<div class="operation-hint">查找/插入/删除平均 O(1),最坏 O(n)</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else-if="activeStructure === '树'"
|
||||
class="tree-visual"
|
||||
>
|
||||
<div v-else-if="activeStructure === '树'" class="tree-visual">
|
||||
<div class="tree-container">
|
||||
<div class="tree-level">
|
||||
<div class="tree-node root">
|
||||
@@ -192,17 +126,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="operation-hint">
|
||||
查找/插入/删除 O(log n),遍历 O(n)
|
||||
</div>
|
||||
<div class="operation-hint">查找/插入/删除 O(log n),遍历 O(n)</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="complexity-table">
|
||||
<div class="table-title">
|
||||
时间复杂度对比
|
||||
</div>
|
||||
<div class="table-title">时间复杂度对比</div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -216,63 +146,31 @@
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>访问</td>
|
||||
<td class="good">
|
||||
O(1)
|
||||
</td>
|
||||
<td class="bad">
|
||||
O(n)
|
||||
</td>
|
||||
<td class="good">
|
||||
O(1)
|
||||
</td>
|
||||
<td class="mid">
|
||||
O(log n)
|
||||
</td>
|
||||
<td class="good">O(1)</td>
|
||||
<td class="bad">O(n)</td>
|
||||
<td class="good">O(1)</td>
|
||||
<td class="mid">O(log n)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>查找</td>
|
||||
<td class="bad">
|
||||
O(n)
|
||||
</td>
|
||||
<td class="bad">
|
||||
O(n)
|
||||
</td>
|
||||
<td class="good">
|
||||
O(1)
|
||||
</td>
|
||||
<td class="mid">
|
||||
O(log n)
|
||||
</td>
|
||||
<td class="bad">O(n)</td>
|
||||
<td class="bad">O(n)</td>
|
||||
<td class="good">O(1)</td>
|
||||
<td class="mid">O(log n)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>插入</td>
|
||||
<td class="bad">
|
||||
O(n)
|
||||
</td>
|
||||
<td class="good">
|
||||
O(1)
|
||||
</td>
|
||||
<td class="good">
|
||||
O(1)
|
||||
</td>
|
||||
<td class="mid">
|
||||
O(log n)
|
||||
</td>
|
||||
<td class="bad">O(n)</td>
|
||||
<td class="good">O(1)</td>
|
||||
<td class="good">O(1)</td>
|
||||
<td class="mid">O(log n)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>删除</td>
|
||||
<td class="bad">
|
||||
O(n)
|
||||
</td>
|
||||
<td class="good">
|
||||
O(1)
|
||||
</td>
|
||||
<td class="good">
|
||||
O(1)
|
||||
</td>
|
||||
<td class="mid">
|
||||
O(log n)
|
||||
</td>
|
||||
<td class="bad">O(n)</td>
|
||||
<td class="good">O(1)</td>
|
||||
<td class="good">O(1)</td>
|
||||
<td class="mid">O(log n)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -280,7 +178,8 @@
|
||||
</div>
|
||||
|
||||
<div class="info-box">
|
||||
<strong>核心思想:</strong>数据结构是数据的"容器",不同的容器有不同的特点。选择合适的数据结构,能让程序效率提升几个数量级。
|
||||
<strong>核心思想:</strong
|
||||
>数据结构是数据的"容器",不同的容器有不同的特点。选择合适的数据结构,能让程序效率提升几个数量级。
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -300,7 +199,7 @@ const structures = [
|
||||
]
|
||||
|
||||
const currentStructure = computed(() => {
|
||||
return structures.find(s => s.name === activeStructure.value)
|
||||
return structures.find((s) => s.name === activeStructure.value)
|
||||
})
|
||||
|
||||
const arrayData = ref([10, 20, 30, 40, 50, 60, 70, 80])
|
||||
@@ -382,8 +281,15 @@ const treeData = ref({
|
||||
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;
|
||||
}
|
||||
|
||||
.structure-tabs {
|
||||
display: flex;
|
||||
@@ -500,7 +406,8 @@ const treeData = ref({
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
.stack-ops, .queue-ops {
|
||||
.stack-ops,
|
||||
.queue-ops {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
margin-top: 0.5rem;
|
||||
@@ -626,7 +533,8 @@ table {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
th, td {
|
||||
th,
|
||||
td {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
padding: 0.35rem;
|
||||
text-align: center;
|
||||
@@ -636,9 +544,16 @@ th {
|
||||
background: var(--vp-c-bg);
|
||||
}
|
||||
|
||||
.good { color: var(--vp-c-success); font-weight: bold; }
|
||||
.mid { color: var(--vp-c-warning); }
|
||||
.bad { color: var(--vp-c-danger); }
|
||||
.good {
|
||||
color: var(--vp-c-success);
|
||||
font-weight: bold;
|
||||
}
|
||||
.mid {
|
||||
color: var(--vp-c-warning);
|
||||
}
|
||||
.bad {
|
||||
color: var(--vp-c-danger);
|
||||
}
|
||||
|
||||
.info-box {
|
||||
background: var(--vp-c-bg-alt);
|
||||
@@ -650,5 +565,4 @@ th {
|
||||
display: flex;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
+16
-19
@@ -152,11 +152,7 @@ const categories = [
|
||||
icon: '🗂️',
|
||||
desc: '通过关键词快速查找',
|
||||
examples: ['哈希表', '字典', '集合'],
|
||||
features: [
|
||||
'通过键值对存储数据',
|
||||
'查找速度极快',
|
||||
'数据之间没有顺序关系'
|
||||
],
|
||||
features: ['通过键值对存储数据', '查找速度极快', '数据之间没有顺序关系'],
|
||||
scenarios: [
|
||||
{
|
||||
icon: '📖',
|
||||
@@ -189,11 +185,7 @@ const categories = [
|
||||
icon: '🌳',
|
||||
desc: '层级关系,像家谱',
|
||||
examples: ['二叉树', 'B 树', '堆'],
|
||||
features: [
|
||||
'一对多的层级关系',
|
||||
'有明确的根节点',
|
||||
'适合表示分类和层级'
|
||||
],
|
||||
features: ['一对多的层级关系', '有明确的根节点', '适合表示分类和层级'],
|
||||
scenarios: [
|
||||
{
|
||||
icon: '📁',
|
||||
@@ -226,11 +218,7 @@ const categories = [
|
||||
icon: '🕸️',
|
||||
desc: '复杂关系网络',
|
||||
examples: ['有向图', '无向图', '网络图'],
|
||||
features: [
|
||||
'多对多的复杂关系',
|
||||
'节点之间可以任意连接',
|
||||
'可以表示复杂网络'
|
||||
],
|
||||
features: ['多对多的复杂关系', '节点之间可以任意连接', '可以表示复杂网络'],
|
||||
scenarios: [
|
||||
{
|
||||
icon: '🗺️',
|
||||
@@ -259,7 +247,9 @@ const categories = [
|
||||
}
|
||||
]
|
||||
|
||||
const currentCategory = computed(() => categories.find(c => c.id === activeCategory.value))
|
||||
const currentCategory = computed(() =>
|
||||
categories.find((c) => c.id === activeCategory.value)
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -278,8 +268,14 @@ const currentCategory = computed(() => categories.find(c => c.id === activeCateg
|
||||
margin-bottom: 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; }
|
||||
.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;
|
||||
@@ -383,7 +379,8 @@ const currentCategory = computed(() => categories.find(c => c.id === activeCateg
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.detail-section {}
|
||||
.detail-section {
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-weight: 600;
|
||||
|
||||
+24
-26
@@ -24,7 +24,9 @@
|
||||
<!-- 推荐结果 -->
|
||||
<div v-if="activeScenario" class="recommendation">
|
||||
<div class="rec-header">
|
||||
<span class="rec-title">推荐使用:{{ currentScenario.recommendation }}</span>
|
||||
<span class="rec-title"
|
||||
>推荐使用:{{ currentScenario.recommendation }}</span
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="rec-reason">
|
||||
@@ -153,11 +155,7 @@ const scenarios = [
|
||||
name: '后进先出',
|
||||
desc: '最后进入的最先处理',
|
||||
recommendation: '栈',
|
||||
reasons: [
|
||||
'只能在栈顶操作',
|
||||
'入栈出栈都是 O(1)',
|
||||
'适合回溯和撤销操作'
|
||||
],
|
||||
reasons: ['只能在栈顶操作', '入栈出栈都是 O(1)', '适合回溯和撤销操作'],
|
||||
example: '浏览器后退、编辑器撤销、函数调用栈'
|
||||
},
|
||||
{
|
||||
@@ -166,11 +164,7 @@ const scenarios = [
|
||||
name: '先进先出',
|
||||
desc: '先来的先处理',
|
||||
recommendation: '队列',
|
||||
reasons: [
|
||||
'一端入队,另一端出队',
|
||||
'入队出队都是 O(1)',
|
||||
'公平的调度方式'
|
||||
],
|
||||
reasons: ['一端入队,另一端出队', '入队出队都是 O(1)', '公平的调度方式'],
|
||||
example: '打印队列、任务调度、消息队列'
|
||||
},
|
||||
{
|
||||
@@ -179,11 +173,7 @@ const scenarios = [
|
||||
name: '层级关系',
|
||||
desc: '数据之间有父子层级关系',
|
||||
recommendation: '树',
|
||||
reasons: [
|
||||
'清晰表达层级结构',
|
||||
'查找效率 O(log n)',
|
||||
'支持多种遍历方式'
|
||||
],
|
||||
reasons: ['清晰表达层级结构', '查找效率 O(log n)', '支持多种遍历方式'],
|
||||
example: '文件系统、组织架构、HTML DOM'
|
||||
},
|
||||
{
|
||||
@@ -192,11 +182,7 @@ const scenarios = [
|
||||
name: '复杂关系',
|
||||
desc: '数据之间有多对多的复杂连接',
|
||||
recommendation: '图',
|
||||
reasons: [
|
||||
'可以表示任意关系',
|
||||
'支持路径搜索算法',
|
||||
'适合网络和社交关系'
|
||||
],
|
||||
reasons: ['可以表示任意关系', '支持路径搜索算法', '适合网络和社交关系'],
|
||||
example: '社交网络、地图导航、网页链接'
|
||||
}
|
||||
]
|
||||
@@ -211,7 +197,7 @@ const referenceTable = [
|
||||
]
|
||||
|
||||
const currentScenario = computed(() => {
|
||||
return scenarios.find(s => s.id === activeScenario.value)
|
||||
return scenarios.find((s) => s.id === activeScenario.value)
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -231,8 +217,14 @@ const currentScenario = computed(() => {
|
||||
margin-bottom: 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; }
|
||||
.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;
|
||||
@@ -298,8 +290,14 @@ const currentScenario = computed(() => {
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
from { opacity: 0; transform: translateY(-10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.rec-header {
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
|
||||
<div class="demo-content">
|
||||
<div class="encoding-tabs">
|
||||
<button
|
||||
v-for="tab in tabs"
|
||||
<button
|
||||
v-for="tab in tabs"
|
||||
:key="tab.name"
|
||||
:class="['tab-btn', { active: activeTab === tab.name }]"
|
||||
@click="activeTab = tab.name"
|
||||
@@ -20,24 +20,19 @@
|
||||
<div class="encoding-area">
|
||||
<div class="input-section">
|
||||
<label>输入内容:</label>
|
||||
<input
|
||||
v-model="inputValue"
|
||||
<input
|
||||
v-model="inputValue"
|
||||
class="input-field"
|
||||
:placeholder="currentTab.placeholder"
|
||||
>
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="output-section">
|
||||
<div class="output-label">
|
||||
编码结果:
|
||||
</div>
|
||||
<div class="output-label">编码结果:</div>
|
||||
<div class="output-box">
|
||||
<code>{{ encodedResult }}</code>
|
||||
</div>
|
||||
<div
|
||||
v-if="currentTab.name === 'text'"
|
||||
class="output-info"
|
||||
>
|
||||
<div v-if="currentTab.name === 'text'" class="output-info">
|
||||
<span>字符数: {{ inputValue.length }}</span>
|
||||
<span>字节数: {{ byteCount }}</span>
|
||||
</div>
|
||||
@@ -47,18 +42,22 @@
|
||||
v-if="currentTab.name === 'text' && inputValue"
|
||||
class="encoding-table"
|
||||
>
|
||||
<div class="table-title">
|
||||
字符编码详情
|
||||
</div>
|
||||
<div class="table-title">字符编码详情</div>
|
||||
<div class="char-list">
|
||||
<div
|
||||
v-for="(char, i) in inputValue.slice(0, 10)"
|
||||
<div
|
||||
v-for="(char, i) in inputValue.slice(0, 10)"
|
||||
:key="i"
|
||||
class="char-item"
|
||||
>
|
||||
<span class="char-display">{{ char }}</span>
|
||||
<span class="char-unicode">U+{{ char.charCodeAt(0).toString(16).toUpperCase().padStart(4, '0') }}</span>
|
||||
<span class="char-binary">{{ char.charCodeAt(0).toString(2).padStart(8, '0') }}</span>
|
||||
<span class="char-unicode"
|
||||
>U+{{
|
||||
char.charCodeAt(0).toString(16).toUpperCase().padStart(4, '0')
|
||||
}}</span
|
||||
>
|
||||
<span class="char-binary">{{
|
||||
char.charCodeAt(0).toString(2).padStart(8, '0')
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -66,7 +65,9 @@
|
||||
</div>
|
||||
|
||||
<div class="info-box">
|
||||
<strong>核心思想:</strong>所有数据最终都要变成 0 和 1。不同类型的数据用不同的编码规则:字符用 ASCII/Unicode,数字用二进制,图像用像素值。
|
||||
<strong>核心思想:</strong>所有数据最终都要变成 0 和
|
||||
1。不同类型的数据用不同的编码规则:字符用
|
||||
ASCII/Unicode,数字用二进制,图像用像素值。
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -84,21 +85,25 @@ const tabs = [
|
||||
]
|
||||
|
||||
const currentTab = computed(() => {
|
||||
const tab = tabs.find(t => t.name === activeTab.value)
|
||||
const tab = tabs.find((t) => t.name === activeTab.value)
|
||||
return {
|
||||
...tab,
|
||||
placeholder: tab.name === 'text' ? '输入文字...' :
|
||||
tab.name === 'number' ? '输入数字...' : '输入颜色值(如 #FF5733)'
|
||||
placeholder:
|
||||
tab.name === 'text'
|
||||
? '输入文字...'
|
||||
: tab.name === 'number'
|
||||
? '输入数字...'
|
||||
: '输入颜色值(如 #FF5733)'
|
||||
}
|
||||
})
|
||||
|
||||
const encodedResult = computed(() => {
|
||||
if (!inputValue.value) return ''
|
||||
|
||||
|
||||
switch (activeTab.value) {
|
||||
case 'text':
|
||||
return Array.from(inputValue.value)
|
||||
.map(c => c.charCodeAt(0).toString(2).padStart(8, '0'))
|
||||
.map((c) => c.charCodeAt(0).toString(2).padStart(8, '0'))
|
||||
.join(' ')
|
||||
case 'number':
|
||||
const num = parseInt(inputValue.value)
|
||||
@@ -137,8 +142,15 @@ const byteCount = computed(() => {
|
||||
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;
|
||||
}
|
||||
|
||||
.encoding-tabs {
|
||||
display: flex;
|
||||
@@ -269,5 +281,4 @@ const byteCount = computed(() => {
|
||||
display: flex;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
+47
-13
@@ -35,7 +35,9 @@
|
||||
<div class="arrow">↓</div>
|
||||
<div class="output-box">
|
||||
<div class="box-label">编码后</div>
|
||||
<div class="box-value code">{{ currentScenario.encoding.output }}</div>
|
||||
<div class="box-value code">
|
||||
{{ currentScenario.encoding.output }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -62,11 +64,15 @@
|
||||
<div class="storage-info">
|
||||
<div class="info-item">
|
||||
<span class="info-label">位置:</span>
|
||||
<span class="info-value">{{ currentScenario.storage.location }}</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>
|
||||
<span class="info-value">{{
|
||||
currentScenario.storage.size
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -83,7 +89,12 @@
|
||||
<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">
|
||||
<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>
|
||||
@@ -93,11 +104,15 @@
|
||||
<div class="transmission-info">
|
||||
<div class="info-item">
|
||||
<span class="info-label">协议:</span>
|
||||
<span class="info-value">{{ currentScenario.transmission.protocol }}</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>
|
||||
<span class="info-value">{{
|
||||
currentScenario.transmission.path
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -107,11 +122,15 @@
|
||||
<!-- 协作关系 -->
|
||||
<div class="collab-relationships">
|
||||
<div class="relationship-arrow encoding-to-storage">
|
||||
<span class="arrow-text">{{ currentScenario.relationships.encodingToStorage }}</span>
|
||||
<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-text">{{
|
||||
currentScenario.relationships.storageToTransmission
|
||||
}}</span>
|
||||
<span class="arrow-icon">→</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -121,7 +140,11 @@
|
||||
<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
|
||||
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>
|
||||
@@ -347,8 +370,14 @@ const currentScenario = computed(() => scenarioData[activeScenario.value])
|
||||
margin-bottom: 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; }
|
||||
.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;
|
||||
@@ -420,8 +449,13 @@ const currentScenario = computed(() => scenarioData[activeScenario.value])
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.stage-icon { font-size: 1.3rem; }
|
||||
.stage-title { font-weight: 600; font-size: 0.95rem; }
|
||||
.stage-icon {
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
.stage-title {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.stage-content {
|
||||
display: flex;
|
||||
|
||||
+52
-33
@@ -1,34 +1,32 @@
|
||||
<template>
|
||||
<div class="filesystem-demo">
|
||||
<div class="demo-wrapper">
|
||||
|
||||
<!-- 文件树:逻辑视角 -->
|
||||
<div class="logical-view">
|
||||
<div class="view-title">
|
||||
<span>📁 你的视角 (文件系统)</span>
|
||||
<span class="subtitle">漂亮、整洁的目录树</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="file-tree">
|
||||
<div class="tree-node folder expanded">
|
||||
<span class="icon">💾</span> D盘 (根目录)
|
||||
</div>
|
||||
<div class="tree-children">
|
||||
|
||||
<div class="tree-node folder expanded">
|
||||
<span class="icon">📂</span> 照片
|
||||
</div>
|
||||
<div class="tree-children">
|
||||
<div
|
||||
class="tree-node file"
|
||||
<div
|
||||
class="tree-node file"
|
||||
:class="{ active: activeFile === 'pet' }"
|
||||
@click="selectFile('pet')"
|
||||
>
|
||||
<span class="icon">🖼️</span> 宠物.jpg
|
||||
<span class="size-badge">3 块</span>
|
||||
</div>
|
||||
<div
|
||||
class="tree-node file"
|
||||
<div
|
||||
class="tree-node file"
|
||||
:class="{ active: activeFile === 'vacation' }"
|
||||
@click="selectFile('vacation')"
|
||||
>
|
||||
@@ -41,8 +39,8 @@
|
||||
<span class="icon">📂</span> 工作
|
||||
</div>
|
||||
<div class="tree-children">
|
||||
<div
|
||||
class="tree-node file"
|
||||
<div
|
||||
class="tree-node file"
|
||||
:class="{ active: activeFile === 'doc' }"
|
||||
@click="selectFile('doc')"
|
||||
>
|
||||
@@ -50,7 +48,6 @@
|
||||
<span class="size-badge">4 块</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -58,7 +55,7 @@
|
||||
<!-- 翻译官动画 -->
|
||||
<div class="translator">
|
||||
<div class="arrow"></div>
|
||||
<div class="badge">文件系统账本<br/>(inode表)</div>
|
||||
<div class="badge">文件系统账本<br />(inode表)</div>
|
||||
<div class="arrow"></div>
|
||||
</div>
|
||||
|
||||
@@ -68,39 +65,36 @@
|
||||
<span>🖨️ 硬盘的视角 (物理存储)</span>
|
||||
<span class="subtitle">无序、零散的数据块</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="disk-grid">
|
||||
<div
|
||||
v-for="block in 24"
|
||||
<div
|
||||
v-for="block in 24"
|
||||
:key="block"
|
||||
class="disk-block"
|
||||
:class="[
|
||||
getBlockOwner(block),
|
||||
{ active: isBlockActive(block) }
|
||||
]"
|
||||
:class="[getBlockOwner(block), { active: isBlockActive(block) }]"
|
||||
>
|
||||
{{ block }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="explanation-box" v-if="activeFile">
|
||||
<span v-if="activeFile === 'pet'">
|
||||
💡 宠物.jpg 其实被切碎分别放在了第 3、8、14 块。文件系统帮你做好了翻译,你只需双击它!
|
||||
💡 宠物.jpg 其实被切碎分别放在了第 3、8、14
|
||||
块。文件系统帮你做好了翻译,你只需双击它!
|
||||
</span>
|
||||
<span v-if="activeFile === 'vacation'">
|
||||
💡 旅游.png 放在了第 5、6 块。
|
||||
</span>
|
||||
<span v-if="activeFile === 'doc'">
|
||||
💡 总结.docx 被分散存放在 10、11、18、22 块,如果没有文件系统,你得自己背下这些数字才能打开文件。
|
||||
💡 总结.docx 被分散存放在 10、11、18、22
|
||||
块,如果没有文件系统,你得自己背下这些数字才能打开文件。
|
||||
</span>
|
||||
</div>
|
||||
<div class="explanation-box default" v-else>
|
||||
☝️ 试着点击左侧的文件,看看它们在硬盘里到底长什么样。
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -160,7 +154,8 @@ const isBlockActive = (block) => {
|
||||
}
|
||||
}
|
||||
|
||||
.logical-view, .physical-view {
|
||||
.logical-view,
|
||||
.physical-view {
|
||||
flex: 1;
|
||||
background: var(--vp-c-bg-alt);
|
||||
border-radius: 10px;
|
||||
@@ -278,20 +273,38 @@ const isBlockActive = (block) => {
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.disk-block.owner-pet { background: rgba(16, 185, 129, 0.1); border-color: rgba(16, 185, 129, 0.3); }
|
||||
.disk-block.owner-vacation { background: rgba(59, 130, 246, 0.1); border-color: rgba(59, 130, 246, 0.3); }
|
||||
.disk-block.owner-doc { background: rgba(245, 158, 11, 0.1); border-color: rgba(245, 158, 11, 0.3); }
|
||||
.disk-block.owner-pet {
|
||||
background: rgba(16, 185, 129, 0.1);
|
||||
border-color: rgba(16, 185, 129, 0.3);
|
||||
}
|
||||
.disk-block.owner-vacation {
|
||||
background: rgba(59, 130, 246, 0.1);
|
||||
border-color: rgba(59, 130, 246, 0.3);
|
||||
}
|
||||
.disk-block.owner-doc {
|
||||
background: rgba(245, 158, 11, 0.1);
|
||||
border-color: rgba(245, 158, 11, 0.3);
|
||||
}
|
||||
|
||||
.disk-block.active {
|
||||
transform: scale(1.1);
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
z-index: 2;
|
||||
}
|
||||
.disk-block.owner-pet.active { background: var(--vp-c-success-1); border-color: var(--vp-c-success-1); }
|
||||
.disk-block.owner-vacation.active { background: var(--vp-c-brand-1); border-color: var(--vp-c-brand-1); }
|
||||
.disk-block.owner-doc.active { background: var(--vp-c-warning-1); border-color: var(--vp-c-warning-1); }
|
||||
.disk-block.owner-pet.active {
|
||||
background: var(--vp-c-success-1);
|
||||
border-color: var(--vp-c-success-1);
|
||||
}
|
||||
.disk-block.owner-vacation.active {
|
||||
background: var(--vp-c-brand-1);
|
||||
border-color: var(--vp-c-brand-1);
|
||||
}
|
||||
.disk-block.owner-doc.active {
|
||||
background: var(--vp-c-warning-1);
|
||||
border-color: var(--vp-c-warning-1);
|
||||
}
|
||||
|
||||
.explanation-box {
|
||||
padding: 1rem;
|
||||
@@ -308,7 +321,13 @@ const isBlockActive = (block) => {
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateX(-10px); }
|
||||
to { opacity: 1; transform: translateX(0); }
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(-10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
+4
-2
@@ -20,7 +20,8 @@
|
||||
<!-- MUX Demo -->
|
||||
<div v-if="currentTab === 'mux'" class="demo-panel">
|
||||
<div class="panel-desc">
|
||||
<strong>多路选择器 (MUX)</strong>:像铁路道岔一样,根据"选择信号"决定让哪一路数据通过。
|
||||
<strong>多路选择器 (MUX)</strong
|
||||
>:像铁路道岔一样,根据"选择信号"决定让哪一路数据通过。
|
||||
</div>
|
||||
<div class="mux-container">
|
||||
<div class="inputs">
|
||||
@@ -80,7 +81,8 @@
|
||||
<!-- Decoder Demo -->
|
||||
<div v-if="currentTab === 'decoder'" class="demo-panel">
|
||||
<div class="panel-desc">
|
||||
<strong>译码器 (Decoder)</strong>:将二进制输入转换为特定输出线的激活信号(例如 2位输入可以激活
|
||||
<strong>译码器 (Decoder)</strong
|
||||
>:将二进制输入转换为特定输出线的激活信号(例如 2位输入可以激活
|
||||
4根输出线中的一根)。
|
||||
</div>
|
||||
<div class="decoder-container">
|
||||
|
||||
+14
-4
@@ -69,7 +69,11 @@
|
||||
:cx="node.x"
|
||||
:cy="node.y"
|
||||
r="20"
|
||||
:fill="selectedNode === index ? 'var(--vp-c-brand)' : 'var(--vp-c-brand-soft)'"
|
||||
:fill="
|
||||
selectedNode === index
|
||||
? 'var(--vp-c-brand)'
|
||||
: 'var(--vp-c-brand-soft)'
|
||||
"
|
||||
stroke="var(--vp-c-brand)"
|
||||
stroke-width="2"
|
||||
/>
|
||||
@@ -154,7 +158,7 @@ const edges = ref([
|
||||
])
|
||||
|
||||
const averageDegree = computed(() => {
|
||||
return (edges.value.length * 2 / nodes.length).toFixed(1)
|
||||
return ((edges.value.length * 2) / nodes.length).toFixed(1)
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -174,8 +178,14 @@ const averageDegree = computed(() => {
|
||||
margin-bottom: 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; }
|
||||
.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;
|
||||
|
||||
+63
-14
@@ -8,7 +8,7 @@
|
||||
<div class="core-idea">
|
||||
<div class="idea-box">
|
||||
<div class="idea-text">
|
||||
贪心算法在每一步选择中都采取当前状态下<strong>最优</strong>的选择<br>
|
||||
贪心算法在每一步选择中都采取当前状态下<strong>最优</strong>的选择<br />
|
||||
希望通过一系列局部最优选择达到<strong>全局最优</strong>
|
||||
</div>
|
||||
</div>
|
||||
@@ -36,7 +36,11 @@
|
||||
需要找零:<span class="amount">{{ changeAmount }}</span> 元
|
||||
</div>
|
||||
<div class="change-process">
|
||||
<div class="process-step" v-for="(step, index) in changeSteps" :key="index">
|
||||
<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>
|
||||
@@ -46,7 +50,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="scenario-note">
|
||||
✓ 贪心策略:每次选择面值最大的硬币<br>
|
||||
✓ 贪心策略:每次选择面值最大的硬币<br />
|
||||
✓ 适用于人民币、美元等货币系统
|
||||
</div>
|
||||
</div>
|
||||
@@ -59,9 +63,14 @@
|
||||
<div
|
||||
v-for="(activity, index) in activities"
|
||||
:key="index"
|
||||
:class="['activity-item', { selected: activity.selected, conflicting: activity.conflicting }]"
|
||||
:class="[
|
||||
'activity-item',
|
||||
{ selected: activity.selected, conflicting: activity.conflicting }
|
||||
]"
|
||||
>
|
||||
<div class="activity-time">{{ activity.start }} - {{ activity.end }}</div>
|
||||
<div class="activity-time">
|
||||
{{ activity.start }} - {{ activity.end }}
|
||||
</div>
|
||||
<div class="activity-name">{{ activity.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -183,17 +192,51 @@ const changeSteps = [
|
||||
{ coin: '1元', count: 2, value: 2 }
|
||||
]
|
||||
|
||||
const totalCoins = computed(() => changeSteps.reduce((sum, step) => sum + step.count, 0))
|
||||
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 }
|
||||
{
|
||||
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)
|
||||
const selectedCount = computed(
|
||||
() => activities.filter((a) => a.selected).length
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -212,8 +255,14 @@ const selectedCount = computed(() => activities.filter(a => a.selected).length)
|
||||
margin-bottom: 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; }
|
||||
.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;
|
||||
|
||||
@@ -0,0 +1,333 @@
|
||||
<template>
|
||||
<div class="half-adder-demo">
|
||||
<div class="demo-label">
|
||||
半加器 (Half Adder) 内部构造 ── 尝试组合 A 和 B,观察 XOR(异或门)和
|
||||
AND(与门)的分工
|
||||
</div>
|
||||
|
||||
<div class="circuit-container">
|
||||
<!-- 输入端 -->
|
||||
<div class="inputs">
|
||||
<div class="input-line">
|
||||
<button
|
||||
class="toggle-btn"
|
||||
:class="{ on: inputA }"
|
||||
@click="inputA = !inputA"
|
||||
>
|
||||
{{ inputA ? '1' : '0' }}
|
||||
</button>
|
||||
<span class="label">输入 A</span>
|
||||
</div>
|
||||
<div class="input-line">
|
||||
<button
|
||||
class="toggle-btn"
|
||||
:class="{ on: inputB }"
|
||||
@click="inputB = !inputB"
|
||||
>
|
||||
{{ inputB ? '1' : '0' }}
|
||||
</button>
|
||||
<span class="label">输入 B</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 连线区域 -->
|
||||
<div class="wires">
|
||||
<!-- Path visualization can be complex, using simple SVG lines -->
|
||||
<svg class="wire-svg" viewBox="0 0 100 150" preserveAspectRatio="none">
|
||||
<!-- A to XOR -->
|
||||
<path
|
||||
d="M 0,30 C 50,30 50,40 100,40"
|
||||
fill="none"
|
||||
:stroke="inputA ? 'var(--vp-c-brand-1)' : 'var(--vp-c-text-3)'"
|
||||
stroke-width="2"
|
||||
/>
|
||||
<!-- B to XOR -->
|
||||
<path
|
||||
d="M 0,120 C 50,120 50,60 100,60"
|
||||
fill="none"
|
||||
:stroke="inputB ? 'var(--vp-c-brand-1)' : 'var(--vp-c-text-3)'"
|
||||
stroke-width="2"
|
||||
/>
|
||||
|
||||
<!-- A to AND -->
|
||||
<path
|
||||
d="M 20,30 L 20,90 C 20,90 50,90 100,90"
|
||||
fill="none"
|
||||
:stroke="inputA ? 'var(--vp-c-brand-1)' : 'var(--vp-c-text-3)'"
|
||||
stroke-width="2"
|
||||
/>
|
||||
<!-- B to AND -->
|
||||
<path
|
||||
d="M 40,120 L 40,110 C 40,110 50,110 100,110"
|
||||
fill="none"
|
||||
:stroke="inputB ? 'var(--vp-c-brand-1)' : 'var(--vp-c-text-3)'"
|
||||
stroke-width="2"
|
||||
/>
|
||||
|
||||
<!-- Nodes -->
|
||||
<circle
|
||||
cx="20"
|
||||
cy="30"
|
||||
r="3"
|
||||
:fill="inputA ? 'var(--vp-c-brand-1)' : 'var(--vp-c-text-3)'"
|
||||
/>
|
||||
<circle
|
||||
cx="40"
|
||||
cy="120"
|
||||
r="3"
|
||||
:fill="inputB ? 'var(--vp-c-brand-1)' : 'var(--vp-c-text-3)'"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<!-- 逻辑门 -->
|
||||
<div class="gates">
|
||||
<div class="gate-box xor-gate" :class="{ active: sumOut }">
|
||||
<div class="gate-name">XOR 门</div>
|
||||
<div class="gate-desc">计算"本位" (相加结果)</div>
|
||||
</div>
|
||||
<div class="gate-box and-gate" :class="{ active: carryOut }">
|
||||
<div class="gate-name">AND 门</div>
|
||||
<div class="gate-desc">计算"进位" (满2进1)</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 线 -->
|
||||
<div class="wires outputs-wires">
|
||||
<svg class="wire-svg" viewBox="0 0 50 150" preserveAspectRatio="none">
|
||||
<line
|
||||
x1="0"
|
||||
y1="50"
|
||||
x2="50"
|
||||
y2="50"
|
||||
:stroke="
|
||||
sumOut ? 'var(--vp-c-green-1, #16a34a)' : 'var(--vp-c-text-3)'
|
||||
"
|
||||
stroke-width="2"
|
||||
/>
|
||||
<line
|
||||
x1="0"
|
||||
y1="100"
|
||||
x2="50"
|
||||
y2="100"
|
||||
:stroke="carryOut ? '#d97706' : 'var(--vp-c-text-3)'"
|
||||
stroke-width="2"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<!-- 输出端 -->
|
||||
<div class="outputs">
|
||||
<div class="output-line" :class="{ active: sumOut }">
|
||||
<span class="label">本位 (Sum)</span>
|
||||
<span class="out-val s-val">{{ sumOut ? '1' : '0' }}</span>
|
||||
</div>
|
||||
<div class="output-line" :class="{ active: carryOut }">
|
||||
<span class="label">向前进位 (Carry)</span>
|
||||
<span class="out-val c-val">{{ carryOut ? '1' : '0' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="logic-explain">
|
||||
<p>
|
||||
你的输入是 {{ inputA ? '1' : '0' }} 和 {{ inputB ? '1' : '0' }}。<br />
|
||||
<strong>XOR 门</strong>判断它们不仅要"相加",还看是否"不同":{{
|
||||
inputA !== inputB ? '不同,出1' : '相同,出0'
|
||||
}}
|
||||
——> 核心本位 <strong>{{ sumOut ? '1' : '0' }}</strong
|
||||
>。<br />
|
||||
<strong>AND 门</strong>暗中观察是否"全为真":{{
|
||||
inputA && inputB ? '全为 1,产生进位!' : '没有全为 1,不进位'
|
||||
}}
|
||||
——> 进位信号 <strong>{{ carryOut ? '1' : '0' }}</strong
|
||||
>。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const inputA = ref(false)
|
||||
const inputB = ref(true)
|
||||
|
||||
const sumOut = computed(() => inputA.value !== inputB.value)
|
||||
const carryOut = computed(() => inputA.value && inputB.value)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.half-adder-demo {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
background: var(--vp-c-bg-soft);
|
||||
padding: 1rem 1.2rem;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.demo-label {
|
||||
font-size: 0.78rem;
|
||||
font-weight: bold;
|
||||
color: var(--vp-c-text-2);
|
||||
margin-bottom: 1rem;
|
||||
letter-spacing: 0.2px;
|
||||
}
|
||||
|
||||
.circuit-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0;
|
||||
padding: 1rem;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.inputs,
|
||||
.outputs {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3.5rem;
|
||||
min-width: 6rem;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.outputs {
|
||||
min-width: 8rem;
|
||||
}
|
||||
|
||||
.input-line,
|
||||
.output-line {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.toggle-btn {
|
||||
width: 2.2rem;
|
||||
height: 2.2rem;
|
||||
border-radius: 4px;
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
font-weight: bold;
|
||||
font-family: monospace;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.toggle-btn.on {
|
||||
background: var(--vp-c-brand-soft);
|
||||
color: var(--vp-c-brand-1);
|
||||
border-color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
.out-val {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 2.2rem;
|
||||
height: 2.2rem;
|
||||
border-radius: 4px;
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
font-weight: bold;
|
||||
font-family: monospace;
|
||||
font-size: 1rem;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.s-val {
|
||||
color: var(--vp-c-text-3);
|
||||
}
|
||||
.c-val {
|
||||
color: var(--vp-c-text-3);
|
||||
}
|
||||
|
||||
.output-line.active .s-val {
|
||||
background: #dcfce7;
|
||||
color: #16a34a;
|
||||
border-color: #16a34a;
|
||||
}
|
||||
.output-line.active .c-val {
|
||||
background: #fef3c7;
|
||||
color: #d97706;
|
||||
border-color: #d97706;
|
||||
}
|
||||
|
||||
.wires {
|
||||
width: 80px;
|
||||
height: 150px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.outputs-wires {
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
.wire-svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.gates {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
z-index: 2;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.gate-box {
|
||||
width: 7.5rem;
|
||||
height: 4rem;
|
||||
background: var(--vp-c-bg-alt);
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.gate-box.active {
|
||||
border-color: var(--vp-c-brand-1);
|
||||
box-shadow: 0 0 8px var(--vp-c-brand-soft);
|
||||
}
|
||||
|
||||
.gate-name {
|
||||
font-weight: bold;
|
||||
font-size: 0.9rem;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.gate-desc {
|
||||
font-size: 0.65rem;
|
||||
color: var(--vp-c-text-3);
|
||||
margin-top: 0.2rem;
|
||||
}
|
||||
|
||||
.logic-explain {
|
||||
margin-top: 1.5rem;
|
||||
padding: 0.8rem;
|
||||
background: var(--vp-c-bg);
|
||||
border-radius: 6px;
|
||||
font-size: 0.85rem;
|
||||
text-align: center;
|
||||
color: var(--vp-c-text-2);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.circuit-container {
|
||||
transform: scale(0.85);
|
||||
transform-origin: left top;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -28,9 +28,7 @@
|
||||
placeholder="值 (如: 苹果)"
|
||||
class="hash-input"
|
||||
/>
|
||||
<button @click="addData" class="add-btn">
|
||||
添加
|
||||
</button>
|
||||
<button @click="addData" class="add-btn">添加</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -60,7 +58,10 @@
|
||||
<div
|
||||
v-for="(slot, index) in hashTable"
|
||||
:key="index"
|
||||
:class="['table-slot', { occupied: slot !== null, highlighted: index === exampleIndex }]"
|
||||
:class="[
|
||||
'table-slot',
|
||||
{ occupied: slot !== null, highlighted: index === exampleIndex }
|
||||
]"
|
||||
>
|
||||
<div class="slot-index">{{ index }}</div>
|
||||
<div class="slot-value">{{ slot || '空' }}</div>
|
||||
@@ -121,7 +122,18 @@ const newKey = ref('')
|
||||
const newValue = ref('')
|
||||
const exampleKey = ref('apple')
|
||||
|
||||
const hashTable = ref([null, null, null, null, null, null, null, null, null, null])
|
||||
const hashTable = ref([
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
])
|
||||
|
||||
// 初始化一些数据
|
||||
const initData = () => {
|
||||
@@ -130,7 +142,7 @@ const initData = () => {
|
||||
{ key: 'banana', value: '香蕉' },
|
||||
{ key: 'orange', value: '橙子' }
|
||||
]
|
||||
data.forEach(item => {
|
||||
data.forEach((item) => {
|
||||
const index = simpleHash(item.key)
|
||||
hashTable.value[index] = `${item.key}: ${item.value}`
|
||||
})
|
||||
@@ -174,8 +186,14 @@ initData()
|
||||
margin-bottom: 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; }
|
||||
.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;
|
||||
|
||||
+22
-35
@@ -55,13 +55,17 @@
|
||||
<div class="pros">
|
||||
<div class="list-title">✓ 优点</div>
|
||||
<ul>
|
||||
<li v-for="(pro, index) in currentEra.pros" :key="index">{{ pro }}</li>
|
||||
<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>
|
||||
<li v-for="(con, index) in currentEra.cons" :key="index">
|
||||
{{ con }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -99,11 +103,7 @@
|
||||
<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 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">
|
||||
@@ -134,11 +134,7 @@ const eras = [
|
||||
icon: '0️⃣',
|
||||
position: '5%',
|
||||
example: '10110000 11000000\n(add two numbers)',
|
||||
features: [
|
||||
'直接用二进制代码',
|
||||
'机器可以直接执行',
|
||||
'完全依赖硬件'
|
||||
],
|
||||
features: ['直接用二进制代码', '机器可以直接执行', '完全依赖硬件'],
|
||||
pros: ['执行速度最快', '直接控制硬件'],
|
||||
cons: ['极难编写', '容易出错', '不可移植']
|
||||
},
|
||||
@@ -149,11 +145,7 @@ const eras = [
|
||||
icon: '🔧',
|
||||
position: '25%',
|
||||
example: 'MOV AX, 5\nADD AX, 3\n(add 5 and 3)',
|
||||
features: [
|
||||
'用助记符代替二进制',
|
||||
'需要汇编器翻译',
|
||||
'仍然依赖硬件'
|
||||
],
|
||||
features: ['用助记符代替二进制', '需要汇编器翻译', '仍然依赖硬件'],
|
||||
pros: ['比机器语言好懂', '效率仍然很高'],
|
||||
cons: ['代码冗长', '不可移植', '需要了解硬件']
|
||||
},
|
||||
@@ -164,11 +156,7 @@ const eras = [
|
||||
icon: '📋',
|
||||
position: '50%',
|
||||
example: 'int add(int a, int b) {\n return a + b;\n}',
|
||||
features: [
|
||||
'函数、变量等抽象',
|
||||
'结构化编程',
|
||||
'可移植性好'
|
||||
],
|
||||
features: ['函数、变量等抽象', '结构化编程', '可移植性好'],
|
||||
pros: ['易读易写', '可移植', '效率较高'],
|
||||
cons: ['大型项目难以维护', '代码重用性差']
|
||||
},
|
||||
@@ -179,11 +167,7 @@ const eras = [
|
||||
icon: '🎯',
|
||||
position: '75%',
|
||||
example: 'class Calculator {\n add(a, b) { return a + b; }\n}',
|
||||
features: [
|
||||
'类、对象、封装、继承',
|
||||
'模块化设计',
|
||||
'代码复用性强'
|
||||
],
|
||||
features: ['类、对象、封装、继承', '模块化设计', '代码复用性强'],
|
||||
pros: ['适合大型项目', '易维护', '可扩展'],
|
||||
cons: ['学习曲线陡', '代码量较大']
|
||||
},
|
||||
@@ -194,11 +178,7 @@ const eras = [
|
||||
icon: '🚀',
|
||||
position: '95%',
|
||||
example: 'const add = (a, b) => a + b;\n(add arrow function)',
|
||||
features: [
|
||||
'简洁优雅的语法',
|
||||
'多范式支持',
|
||||
'强大的标准库'
|
||||
],
|
||||
features: ['简洁优雅的语法', '多范式支持', '强大的标准库'],
|
||||
pros: ['开发效率高', '生态丰富', '社区活跃'],
|
||||
cons: ['抽象层多', '性能可能不如底层语言']
|
||||
}
|
||||
@@ -232,8 +212,14 @@ const currentEra = computed(() => eras[activeEra.value])
|
||||
margin-bottom: 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; }
|
||||
.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);
|
||||
@@ -321,7 +307,8 @@ const currentEra = computed(() => eras[activeEra.value])
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.content-section {}
|
||||
.content-section {
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-weight: 600;
|
||||
|
||||
+16
-8
@@ -120,9 +120,7 @@
|
||||
<div class="evo-name">现代语言</div>
|
||||
<div class="evo-arrow">→</div>
|
||||
</div>
|
||||
<div class="evo-result">
|
||||
越来越接近<br>人类思维
|
||||
</div>
|
||||
<div class="evo-result">越来越接近<br />人类思维</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -139,7 +137,8 @@ const scenarios = [
|
||||
icon: '🌐',
|
||||
title: '开发网站',
|
||||
desc: '创建交互式网页',
|
||||
fullDesc: '你需要创建一个在线购物网站,用户可以浏览商品、加入购物车、下单支付',
|
||||
fullDesc:
|
||||
'你需要创建一个在线购物网站,用户可以浏览商品、加入购物车、下单支付',
|
||||
reasons: [
|
||||
'HTML 定义网页结构',
|
||||
'CSS 实现美观样式',
|
||||
@@ -211,7 +210,9 @@ const scenarios = [
|
||||
}
|
||||
]
|
||||
|
||||
const currentScenario = computed(() => scenarios.find(s => s.id === activeScenario.value))
|
||||
const currentScenario = computed(() =>
|
||||
scenarios.find((s) => s.id === activeScenario.value)
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -230,8 +231,14 @@ const currentScenario = computed(() => scenarios.find(s => s.id === activeScenar
|
||||
margin-bottom: 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; }
|
||||
.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;
|
||||
@@ -319,7 +326,8 @@ const currentScenario = computed(() => scenarios.find(s => s.id === activeScenar
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.detail-section {}
|
||||
.detail-section {
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-weight: 600;
|
||||
|
||||
+10
-12
@@ -6,18 +6,10 @@
|
||||
</div>
|
||||
|
||||
<div class="dimension-grid">
|
||||
<div
|
||||
v-for="dim in dimensions"
|
||||
:key="dim.id"
|
||||
class="dimension-card"
|
||||
>
|
||||
<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 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>
|
||||
@@ -111,8 +103,14 @@ const dimensions = [
|
||||
margin-bottom: 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; }
|
||||
.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;
|
||||
|
||||
+15
-15
@@ -76,12 +76,8 @@
|
||||
<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>
|
||||
<button class="op-btn" @click="pushStack">入栈 (PUSH)</button>
|
||||
<button class="op-btn" @click="popStack">出栈 (POP)</button>
|
||||
</div>
|
||||
<div class="visual-note">
|
||||
后进先出 (LIFO) | 应用:撤销操作、函数调用
|
||||
@@ -104,12 +100,8 @@
|
||||
<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>
|
||||
<button class="op-btn" @click="enqueue">入队 (ENQUEUE)</button>
|
||||
<button class="op-btn" @click="dequeue">出队 (DEQUEUE)</button>
|
||||
</div>
|
||||
<div class="visual-note">
|
||||
先进先出 (FIFO) | 应用:任务队列、打印队列
|
||||
@@ -239,7 +231,9 @@ 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 currentStructure = computed(() =>
|
||||
structures.find((s) => s.id === activeStructure.value)
|
||||
)
|
||||
|
||||
const pushStack = () => {
|
||||
const newItem = `书${stackData.value.length + 1}`
|
||||
@@ -280,8 +274,14 @@ const dequeue = () => {
|
||||
margin-bottom: 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; }
|
||||
.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;
|
||||
|
||||
@@ -18,7 +18,12 @@
|
||||
<tr v-for="(row, i) in gate.rows" :key="i">
|
||||
<td>{{ row[0] }}</td>
|
||||
<td v-if="gate.name !== 'NOT'">{{ row[1] }}</td>
|
||||
<td class="result-cell" :class="{ one: row[row.length - 1] === 1 }">{{ row[row.length - 1] }}</td>
|
||||
<td
|
||||
class="result-cell"
|
||||
:class="{ one: row[row.length - 1] === 1 }"
|
||||
>
|
||||
{{ row[row.length - 1] }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -34,22 +39,40 @@ const gates = [
|
||||
{
|
||||
name: 'AND',
|
||||
rule: '都为 1 才得 1',
|
||||
rows: [[0,0,0],[0,1,0],[1,0,0],[1,1,1]]
|
||||
rows: [
|
||||
[0, 0, 0],
|
||||
[0, 1, 0],
|
||||
[1, 0, 0],
|
||||
[1, 1, 1]
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'OR',
|
||||
rule: '有一个 1 就得 1',
|
||||
rows: [[0,0,0],[0,1,1],[1,0,1],[1,1,1]]
|
||||
rows: [
|
||||
[0, 0, 0],
|
||||
[0, 1, 1],
|
||||
[1, 0, 1],
|
||||
[1, 1, 1]
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'NOT',
|
||||
rule: '取反',
|
||||
rows: [[0,1],[1,0]]
|
||||
rows: [
|
||||
[0, 1],
|
||||
[1, 0]
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'XOR',
|
||||
rule: '不同才得 1',
|
||||
rows: [[0,0,0],[0,1,1],[1,0,1],[1,1,0]]
|
||||
rows: [
|
||||
[0, 0, 0],
|
||||
[0, 1, 1],
|
||||
[1, 0, 1],
|
||||
[1, 1, 0]
|
||||
]
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
@@ -1,33 +1,51 @@
|
||||
<template>
|
||||
<div class="memory-demo">
|
||||
<div class="demo-controls">
|
||||
<button class="allocate-btn wechat" @click="allocate('wechat')" :disabled="!hasFreeSpace">
|
||||
<button
|
||||
class="allocate-btn wechat"
|
||||
@click="allocate('wechat')"
|
||||
:disabled="!hasFreeSpace"
|
||||
>
|
||||
+ 给微信分配数据
|
||||
</button>
|
||||
<button class="allocate-btn game" @click="allocate('game')" :disabled="!hasFreeSpace">
|
||||
<button
|
||||
class="allocate-btn game"
|
||||
@click="allocate('game')"
|
||||
:disabled="!hasFreeSpace"
|
||||
>
|
||||
+ 给游戏分配数据
|
||||
</button>
|
||||
<button class="reset-btn" @click="reset">
|
||||
↺ 重置
|
||||
</button>
|
||||
<button class="reset-btn" @click="reset">↺ 重置</button>
|
||||
</div>
|
||||
|
||||
<div class="system-view">
|
||||
<!-- 虚拟内存试图 -->
|
||||
<div class="virtual-cluster">
|
||||
<div class="process-vm wechat">
|
||||
<div class="title">💬 微信的虚拟内存<br/>(它认为自己独占了空间)</div>
|
||||
<div class="title">💬 微信的虚拟内存<br />(它认为自己独占了空间)</div>
|
||||
<div class="vm-blocks">
|
||||
<div v-for="i in 4" :key="'w'+i" class="block" :class="{ filled: wechatBlocks >= i }">
|
||||
<div
|
||||
v-for="i in 4"
|
||||
:key="'w' + i"
|
||||
class="block"
|
||||
:class="{ filled: wechatBlocks >= i }"
|
||||
>
|
||||
{{ wechatBlocks >= i ? '数据 ' + i : '虚拟空闲' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="process-vm game">
|
||||
<div class="title">🎮 游戏的虚拟内存<br/>(它也认为自己独占了空间)</div>
|
||||
<div class="title">
|
||||
🎮 游戏的虚拟内存<br />(它也认为自己独占了空间)
|
||||
</div>
|
||||
<div class="vm-blocks">
|
||||
<div v-for="i in 4" :key="'g'+i" class="block" :class="{ filled: gameBlocks >= i }">
|
||||
<div
|
||||
v-for="i in 4"
|
||||
:key="'g' + i"
|
||||
class="block"
|
||||
:class="{ filled: gameBlocks >= i }"
|
||||
>
|
||||
{{ gameBlocks >= i ? '数据 ' + i : '虚拟空闲' }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -38,17 +56,17 @@
|
||||
<div class="os-page-table">
|
||||
<div class="title">保安大叔 (OS 页表)</div>
|
||||
<div class="table-info">
|
||||
当程序存数据时,<br/>由我暗中转移到真正的物理缝隙里。
|
||||
当程序存数据时,<br />由我暗中转移到真正的物理缝隙里。
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 物理内存 -->
|
||||
<div class="physical-memory">
|
||||
<div class="title">🗄️ 真实的物理内存条<br/>(其实像个大杂烩一样乱)</div>
|
||||
<div class="title">🗄️ 真实的物理内存条<br />(其实像个大杂烩一样乱)</div>
|
||||
<div class="pm-blocks">
|
||||
<div
|
||||
v-for="(block, idx) in physicalBlocks"
|
||||
:key="'p'+idx"
|
||||
<div
|
||||
v-for="(block, idx) in physicalBlocks"
|
||||
:key="'p' + idx"
|
||||
class="block"
|
||||
:class="[block.type, { occupied: block.type !== 'empty' }]"
|
||||
>
|
||||
@@ -57,9 +75,10 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="explanation-box" v-if="wechatBlocks > 0 || gameBlocks > 0">
|
||||
💡 发现了没?尽管右侧真正的物理内存已经被塞得像个狗皮膏药,但在左侧的微信和游戏眼里,自己的内存条永远是连续且干净的。更重要的是,微信绝对访问不到橘色的物理块,保证了安全!
|
||||
💡
|
||||
发现了没?尽管右侧真正的物理内存已经被塞得像个狗皮膏药,但在左侧的微信和游戏眼里,自己的内存条永远是连续且干净的。更重要的是,微信绝对访问不到橘色的物理块,保证了安全!
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -80,20 +99,20 @@ const initialPhysicalBlocks = [
|
||||
{ type: 'empty', label: '空闲' },
|
||||
{ type: 'empty', label: '空闲' },
|
||||
{ type: 'os', label: '系统驱动' },
|
||||
{ type: 'empty', label: '空闲' },
|
||||
{ type: 'empty', label: '空闲' }
|
||||
]
|
||||
|
||||
const physicalBlocks = ref(JSON.parse(JSON.stringify(initialPhysicalBlocks)))
|
||||
|
||||
const freeSpaceCount = computed(() => {
|
||||
return physicalBlocks.value.filter(b => b.type === 'empty').length
|
||||
return physicalBlocks.value.filter((b) => b.type === 'empty').length
|
||||
})
|
||||
|
||||
const hasFreeSpace = computed(() => freeSpaceCount.value > 0)
|
||||
|
||||
const allocate = (process) => {
|
||||
if (!hasFreeSpace.value) return
|
||||
|
||||
|
||||
// Find a process block logic
|
||||
if (process === 'wechat' && wechatBlocks.value < 4) {
|
||||
wechatBlocks.value++
|
||||
@@ -109,9 +128,10 @@ const fillRandomEmptyBlock = (type, label) => {
|
||||
physicalBlocks.value.forEach((b, i) => {
|
||||
if (b.type === 'empty') emptyIndices.push(i)
|
||||
})
|
||||
|
||||
|
||||
if (emptyIndices.length > 0) {
|
||||
const randomIndex = emptyIndices[Math.floor(Math.random() * emptyIndices.length)]
|
||||
const randomIndex =
|
||||
emptyIndices[Math.floor(Math.random() * emptyIndices.length)]
|
||||
physicalBlocks.value[randomIndex] = { type, label }
|
||||
}
|
||||
}
|
||||
@@ -263,7 +283,7 @@ const reset = () => {
|
||||
padding: 1rem;
|
||||
position: relative;
|
||||
border: 2px solid var(--vp-c-brand-1);
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.05);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.os-page-table .table-info {
|
||||
@@ -317,9 +337,17 @@ const reset = () => {
|
||||
}
|
||||
|
||||
@keyframes popIn {
|
||||
0% { transform: scale(0.9); opacity: 0; }
|
||||
50% { transform: scale(1.05); }
|
||||
100% { transform: scale(1); opacity: 1; }
|
||||
0% {
|
||||
transform: scale(0.9);
|
||||
opacity: 0;
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.explanation-box {
|
||||
@@ -333,7 +361,13 @@ const reset = () => {
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
|
||||
<div class="demo-content">
|
||||
<div class="layers-container">
|
||||
<div
|
||||
v-for="(layer, i) in layers"
|
||||
<div
|
||||
v-for="(layer, i) in layers"
|
||||
:key="i"
|
||||
:class="['layer', { active: activeLayer === i }]"
|
||||
@click="activeLayer = i"
|
||||
@@ -24,19 +24,13 @@
|
||||
{{ layer.protocols }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="layer.device"
|
||||
class="layer-device"
|
||||
>
|
||||
<div v-if="layer.device" class="layer-device">
|
||||
{{ layer.device }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="currentLayer"
|
||||
class="layer-detail"
|
||||
>
|
||||
<div v-if="currentLayer" class="layer-detail">
|
||||
<div class="detail-header">
|
||||
<span class="detail-name">{{ currentLayer.name }}</span>
|
||||
<span class="detail-analogy">{{ currentLayer.analogy }}</span>
|
||||
@@ -45,14 +39,9 @@
|
||||
{{ currentLayer.desc }}
|
||||
</div>
|
||||
<div class="detail-tasks">
|
||||
<div class="task-title">
|
||||
核心任务
|
||||
</div>
|
||||
<div class="task-title">核心任务</div>
|
||||
<ul>
|
||||
<li
|
||||
v-for="(task, j) in currentLayer.tasks"
|
||||
:key="j"
|
||||
>
|
||||
<li v-for="(task, j) in currentLayer.tasks" :key="j">
|
||||
{{ task }}
|
||||
</li>
|
||||
</ul>
|
||||
@@ -64,35 +53,25 @@
|
||||
</div>
|
||||
|
||||
<div class="encapsulation-demo">
|
||||
<div class="encap-title">
|
||||
数据封装过程
|
||||
</div>
|
||||
<div class="encap-title">数据封装过程</div>
|
||||
<div class="encap-flow">
|
||||
<div
|
||||
v-for="(step, i) in encapsulation"
|
||||
:key="i"
|
||||
class="encap-step"
|
||||
>
|
||||
<div v-for="(step, i) in encapsulation" :key="i" class="encap-step">
|
||||
<div class="step-layer">
|
||||
{{ step.layer }}
|
||||
</div>
|
||||
<div class="step-data">
|
||||
<span
|
||||
v-if="step.header"
|
||||
class="header"
|
||||
>{{ step.header }}</span>
|
||||
<span v-if="step.header" class="header">{{ step.header }}</span>
|
||||
<span class="payload">{{ step.payload }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="arrow">
|
||||
↓ 发送
|
||||
</div>
|
||||
<div class="arrow">↓ 发送</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-box">
|
||||
<strong>核心思想:</strong>分层设计让网络协议模块化,每层只关心自己的职责。数据从应用层向下传递时,每层都会添加自己的"信封"(头部),接收时再逐层拆开。
|
||||
<strong>核心思想:</strong
|
||||
>分层设计让网络协议模块化,每层只关心自己的职责。数据从应用层向下传递时,每层都会添加自己的"信封"(头部),接收时再逐层拆开。
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -118,7 +97,12 @@ const layers = [
|
||||
device: '',
|
||||
analogy: '包裹分拣组',
|
||||
desc: '负责端到端的数据传输,确保数据的可靠性或实时性。',
|
||||
tasks: ['建立和管理端到端连接', '分段和重组数据', '流量控制和拥塞控制', '端口寻址'],
|
||||
tasks: [
|
||||
'建立和管理端到端连接',
|
||||
'分段和重组数据',
|
||||
'流量控制和拥塞控制',
|
||||
'端口寻址'
|
||||
],
|
||||
unit: '段(Segment)'
|
||||
},
|
||||
{
|
||||
@@ -177,8 +161,15 @@ const encapsulation = [
|
||||
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;
|
||||
}
|
||||
|
||||
.demo-content {
|
||||
display: grid;
|
||||
|
||||
+25
-6
@@ -201,8 +201,14 @@ const protocolLayers = [
|
||||
margin-bottom: 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; }
|
||||
.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;
|
||||
@@ -253,8 +259,14 @@ const protocolLayers = [
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.app-icon { font-size: 1.5rem; }
|
||||
.app-name { font-size: 0.8rem; font-weight: 600; color: var(--vp-c-brand); }
|
||||
.app-icon {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
.app-name {
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.network-path {
|
||||
flex: 1;
|
||||
@@ -316,8 +328,15 @@ const protocolLayers = [
|
||||
}
|
||||
|
||||
@keyframes flowMove {
|
||||
0%, 100% { transform: translateX(-20px); opacity: 0; }
|
||||
50% { transform: translateX(20px); opacity: 1; }
|
||||
0%,
|
||||
100% {
|
||||
transform: translateX(-20px);
|
||||
opacity: 0;
|
||||
}
|
||||
50% {
|
||||
transform: translateX(20px);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.flow-packet {
|
||||
|
||||
+8
-2
@@ -106,8 +106,14 @@ const principles = [
|
||||
margin-bottom: 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; }
|
||||
.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;
|
||||
|
||||
+23
-32
@@ -8,9 +8,7 @@
|
||||
<div class="demo-content">
|
||||
<div class="os-layers">
|
||||
<div class="layer user-apps">
|
||||
<div class="layer-title">
|
||||
应用程序层
|
||||
</div>
|
||||
<div class="layer-title">应用程序层</div>
|
||||
<div class="layer-content">
|
||||
<div
|
||||
v-for="app in applications"
|
||||
@@ -29,9 +27,7 @@
|
||||
</div>
|
||||
|
||||
<div class="layer kernel">
|
||||
<div class="layer-title">
|
||||
操作系统内核
|
||||
</div>
|
||||
<div class="layer-title">操作系统内核</div>
|
||||
<div class="layer-content">
|
||||
<div class="kernel-components">
|
||||
<div
|
||||
@@ -45,15 +41,11 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layer-desc">
|
||||
进程管理、内存管理、文件系统、设备管理
|
||||
</div>
|
||||
<div class="layer-desc">进程管理、内存管理、文件系统、设备管理</div>
|
||||
</div>
|
||||
|
||||
<div class="layer hardware">
|
||||
<div class="layer-title">
|
||||
硬件层
|
||||
</div>
|
||||
<div class="layer-title">硬件层</div>
|
||||
<div class="layer-content">
|
||||
<div class="hardware-icons">
|
||||
<span>💻 CPU</span>
|
||||
@@ -70,27 +62,18 @@
|
||||
<div class="flow-content">
|
||||
<div class="flow-item" :class="{ active: showFlow }">
|
||||
<div class="flow-arrow">↓</div>
|
||||
<div class="flow-desc">
|
||||
应用程序请求资源(内存、CPU、文件)
|
||||
</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 class="flow-desc">操作系统内核统一分配和调度</div>
|
||||
</div>
|
||||
<div class="flow-item" :class="{ active: showFlow }">
|
||||
<div class="flow-arrow">↓</div>
|
||||
<div class="flow-desc">
|
||||
硬件执行实际操作
|
||||
</div>
|
||||
<div class="flow-desc">硬件执行实际操作</div>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
class="flow-btn"
|
||||
@click="showFlow = !showFlow"
|
||||
>
|
||||
<button class="flow-btn" @click="showFlow = !showFlow">
|
||||
{{ showFlow ? '隐藏' : '显示' }}资源流
|
||||
</button>
|
||||
</div>
|
||||
@@ -132,13 +115,13 @@ const kernelComponents = [
|
||||
]
|
||||
|
||||
const activeAppName = computed(() => {
|
||||
const app = applications.find(a => a.id === activeApp.value)
|
||||
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)
|
||||
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} 的功能`
|
||||
@@ -161,8 +144,14 @@ const getActiveAppDesc = () => {
|
||||
margin-bottom: 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; }
|
||||
.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;
|
||||
@@ -213,7 +202,8 @@ const getActiveAppDesc = () => {
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.app-icon:hover, .app-icon.active {
|
||||
.app-icon:hover,
|
||||
.app-icon.active {
|
||||
transform: scale(1.1);
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
@@ -227,7 +217,8 @@ const getActiveAppDesc = () => {
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.kernel-component:hover, .kernel-component.active {
|
||||
.kernel-component:hover,
|
||||
.kernel-component.active {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
+55
-16
@@ -30,32 +30,55 @@
|
||||
<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" />
|
||||
<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="
|
||||
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>
|
||||
<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="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>
|
||||
<text x="420" y="110" fill="var(--vp-c-text-2)" font-size="12">
|
||||
关
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
@@ -114,7 +137,11 @@
|
||||
<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">
|
||||
<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>
|
||||
@@ -138,7 +165,8 @@ 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',
|
||||
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: '较差(易受电磁干扰)',
|
||||
@@ -152,7 +180,8 @@ const mediaData = {
|
||||
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',
|
||||
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: '极强(不受电磁干扰)',
|
||||
@@ -198,8 +227,14 @@ const currentMedia = computed(() => mediaData[activeMedia.value])
|
||||
margin-bottom: 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; }
|
||||
.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;
|
||||
@@ -283,8 +318,12 @@ const currentMedia = computed(() => mediaData[activeMedia.value])
|
||||
}
|
||||
|
||||
@keyframes drawSignal {
|
||||
0% { stroke-dashoffset: 1000; }
|
||||
100% { stroke-dashoffset: 0; }
|
||||
0% {
|
||||
stroke-dashoffset: 1000;
|
||||
}
|
||||
100% {
|
||||
stroke-dashoffset: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.signal-legend {
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
<template>
|
||||
<div class="process-demo">
|
||||
<div class="controls-section">
|
||||
<button class="action-btn" :class="{ active: isRunning }" @click="toggleSimulation">
|
||||
<button
|
||||
class="action-btn"
|
||||
:class="{ active: isRunning }"
|
||||
@click="toggleSimulation"
|
||||
>
|
||||
{{ isRunning ? '⏸ 暂停时间片轮转' : '▶️ 启动 CPU' }}
|
||||
</button>
|
||||
<div class="speed-control">
|
||||
<label>时间流速:</label>
|
||||
<button :class="{ active: speed === 'slow' }" @click="setSpeed('slow')">极慢动作</button>
|
||||
<button :class="{ active: speed === 'fast' }" @click="setSpeed('fast')">真实速度</button>
|
||||
<button :class="{ active: speed === 'slow' }" @click="setSpeed('slow')">
|
||||
极慢动作
|
||||
</button>
|
||||
<button :class="{ active: speed === 'fast' }" @click="setSpeed('fast')">
|
||||
真实速度
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -18,23 +26,21 @@
|
||||
<span v-if="activeProcess" class="task-badge">
|
||||
正在处理: {{ activeProcess.icon }} {{ activeProcess.name }}
|
||||
</span>
|
||||
<span v-else class="task-badge idle">
|
||||
空闲中...
|
||||
</span>
|
||||
<span v-else class="task-badge idle"> 空闲中... </span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 连接线动画 -->
|
||||
<div class="connector">
|
||||
<div
|
||||
class="data-flow"
|
||||
:class="[ `flow-${activeProcessId}`, { running: isRunning }]">
|
||||
</div>
|
||||
<div
|
||||
class="data-flow"
|
||||
:class="[`flow-${activeProcessId}`, { running: isRunning }]"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="processes-grid">
|
||||
<div
|
||||
v-for="p in processes"
|
||||
<div
|
||||
v-for="p in processes"
|
||||
:key="p.id"
|
||||
class="process-card"
|
||||
:class="{ active: p.id === activeProcessId }"
|
||||
@@ -44,21 +50,32 @@
|
||||
<span class="icon">{{ p.icon }}</span>
|
||||
<span class="name">{{ p.name }}</span>
|
||||
</div>
|
||||
<span class="status-badge" :class="p.id === activeProcessId ? 'running' : 'waiting'">
|
||||
<span
|
||||
class="status-badge"
|
||||
:class="p.id === activeProcessId ? 'running' : 'waiting'"
|
||||
>
|
||||
{{ p.id === activeProcessId ? '独占 CPU' : '排队等待' }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="p-progress">
|
||||
<div class="progress-track">
|
||||
<div class="progress-fill" :style="{ width: p.progress + '%' }"></div>
|
||||
<div
|
||||
class="progress-fill"
|
||||
:style="{ width: p.progress + '%' }"
|
||||
></div>
|
||||
</div>
|
||||
<div class="progress-text">{{ Math.floor(p.progress) }}% 完成</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="explanation-box" :class="{ show: isRunning && speed === 'fast' }">
|
||||
💡 **关键启示**:当切换速度足够快时,肉眼已经无法分辨谁在“等待”。这也就是为什么只有一个 CPU 核心的电脑,依然能让你一边听歌一边打字!
|
||||
<div
|
||||
class="explanation-box"
|
||||
:class="{ show: isRunning && speed === 'fast' }"
|
||||
>
|
||||
💡
|
||||
**关键启示**:当切换速度足够快时,肉眼已经无法分辨谁在“等待”。这也就是为什么只有一个
|
||||
CPU 核心的电脑,依然能让你一边听歌一边打字!
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -77,7 +94,9 @@ const processes = ref([
|
||||
{ id: 3, name: '游戏渲染', icon: '🎮', progress: 0 }
|
||||
])
|
||||
|
||||
const activeProcess = computed(() => processes.value.find(p => p.id === activeProcessId.value))
|
||||
const activeProcess = computed(() =>
|
||||
processes.value.find((p) => p.id === activeProcessId.value)
|
||||
)
|
||||
|
||||
const setSpeed = (s) => {
|
||||
speed.value = s
|
||||
@@ -88,17 +107,17 @@ const setSpeed = (s) => {
|
||||
}
|
||||
|
||||
const startLoop = () => {
|
||||
const switchTime = speed.value === 'slow' ? 1200 : 80; // 慢动作 1.2s,快动作极快
|
||||
|
||||
const switchTime = speed.value === 'slow' ? 1200 : 80 // 慢动作 1.2s,快动作极快
|
||||
|
||||
if (!activeProcessId.value) {
|
||||
activeProcessId.value = 1
|
||||
}
|
||||
|
||||
interval = setInterval(() => {
|
||||
// 增加当前进度
|
||||
const curr = processes.value.find(p => p.id === activeProcessId.value)
|
||||
const curr = processes.value.find((p) => p.id === activeProcessId.value)
|
||||
if (curr) {
|
||||
curr.progress += (speed.value === 'slow' ? 15 : 4)
|
||||
curr.progress += speed.value === 'slow' ? 15 : 4
|
||||
if (curr.progress >= 100) curr.progress = 0
|
||||
}
|
||||
|
||||
@@ -106,7 +125,6 @@ const startLoop = () => {
|
||||
let nextId = activeProcessId.value + 1
|
||||
if (nextId > 3) nextId = 1
|
||||
activeProcessId.value = nextId
|
||||
|
||||
}, switchTime)
|
||||
}
|
||||
|
||||
@@ -203,7 +221,7 @@ onUnmounted(() => {
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.05);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
|
||||
transition: all 0.3s;
|
||||
position: relative;
|
||||
}
|
||||
@@ -276,7 +294,9 @@ onUnmounted(() => {
|
||||
.process-card.active::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0; left: 0; right: 0;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 4px;
|
||||
background: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
+36
-17
@@ -7,9 +7,7 @@
|
||||
|
||||
<div class="demo-content">
|
||||
<div class="collab-scene">
|
||||
<div class="scene-title">
|
||||
场景选择:
|
||||
</div>
|
||||
<div class="scene-title">场景选择:</div>
|
||||
<div class="scene-buttons">
|
||||
<button
|
||||
v-for="scene in scenes"
|
||||
@@ -88,7 +86,9 @@
|
||||
>
|
||||
<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>
|
||||
<span v-if="file.size" class="file-size">{{
|
||||
file.size
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -207,19 +207,21 @@ const sceneData = {
|
||||
}
|
||||
}
|
||||
|
||||
const currentSceneData = computed(() => sceneData[activeScene.value] || sceneData.launch)
|
||||
const currentSceneData = computed(
|
||||
() => sceneData[activeScene.value] || sceneData.launch
|
||||
)
|
||||
|
||||
const getProcessName = (id) => {
|
||||
const proc = processes.value.find(p => p.id === id)
|
||||
const proc = processes.value.find((p) => p.id === id)
|
||||
return proc?.name || '系统'
|
||||
}
|
||||
|
||||
const getIcon = (type) => {
|
||||
const icons = {
|
||||
'json': '📋',
|
||||
'db': '🗄️',
|
||||
'folder': '📁',
|
||||
'audio': '🎵'
|
||||
json: '📋',
|
||||
db: '🗄️',
|
||||
folder: '📁',
|
||||
audio: '🎵'
|
||||
}
|
||||
return icons[type] || '📄'
|
||||
}
|
||||
@@ -241,8 +243,14 @@ const getIcon = (type) => {
|
||||
margin-bottom: 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; }
|
||||
.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;
|
||||
@@ -300,8 +308,12 @@ const getIcon = (type) => {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.zone-icon { font-size: 1.2rem; }
|
||||
.zone-name { font-size: 0.85rem; }
|
||||
.zone-icon {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
.zone-name {
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.process-list {
|
||||
display: flex;
|
||||
@@ -324,7 +336,9 @@ const getIcon = (type) => {
|
||||
background: var(--vp-c-brand-soft);
|
||||
}
|
||||
|
||||
.proc-name, .proc-pid, .proc-state {
|
||||
.proc-name,
|
||||
.proc-pid,
|
||||
.proc-state {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
@@ -389,8 +403,13 @@ const getIcon = (type) => {
|
||||
background: var(--vp-c-brand-soft);
|
||||
}
|
||||
|
||||
.file-name { flex: 1; }
|
||||
.file-size { color: var(--vp-c-text-2); font-size: 0.7rem; }
|
||||
.file-name {
|
||||
flex: 1;
|
||||
}
|
||||
.file-size {
|
||||
color: var(--vp-c-text-2);
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
|
||||
.explanation {
|
||||
background: var(--vp-c-bg);
|
||||
|
||||
+22
-6
@@ -65,13 +65,17 @@
|
||||
<div class="pros">
|
||||
<div class="list-title">✓ 优点</div>
|
||||
<ul>
|
||||
<li v-for="(pro, index) in currentLang.pros" :key="index">{{ pro }}</li>
|
||||
<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>
|
||||
<li v-for="(con, index) in currentLang.cons" :key="index">
|
||||
{{ con }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -92,7 +96,11 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(lang, index) in languages" :key="index" :class="{ highlighted: lang.name === activeLang }">
|
||||
<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>
|
||||
@@ -197,7 +205,9 @@ const languages = [
|
||||
}
|
||||
]
|
||||
|
||||
const currentLang = computed(() => languages.find(l => l.name === activeLang.value))
|
||||
const currentLang = computed(() =>
|
||||
languages.find((l) => l.name === activeLang.value)
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -216,8 +226,14 @@ const currentLang = computed(() => languages.find(l => l.name === activeLang.val
|
||||
margin-bottom: 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; }
|
||||
.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;
|
||||
|
||||
+19
-7
@@ -147,7 +147,8 @@ const paradigms = [
|
||||
icon: '📋',
|
||||
desc: '告诉计算机怎么做',
|
||||
idea: '通过一系列命令(语句)来改变程序状态,关注"怎么做"',
|
||||
example: '// 计算1-10的和\nlet sum = 0;\nfor (let i = 1; i <= 10; i++) {\n sum += i;\n}',
|
||||
example:
|
||||
'// 计算1-10的和\nlet sum = 0;\nfor (let i = 1; i <= 10; i++) {\n sum += i;\n}',
|
||||
features: ['变量', '循环', '条件判断', '语句'],
|
||||
languages: ['C', 'Python', 'JavaScript']
|
||||
},
|
||||
@@ -157,7 +158,8 @@ const paradigms = [
|
||||
icon: '🎯',
|
||||
desc: '用对象来组织代码',
|
||||
idea: '将数据和操作数据的方法封装成对象,通过对象间交互来完成任务',
|
||||
example: 'class Calculator {\n add(a, b) { return a + b; }\n}\nconst calc = new Calculator();',
|
||||
example:
|
||||
'class Calculator {\n add(a, b) { return a + b; }\n}\nconst calc = new Calculator();',
|
||||
features: ['封装', '继承', '多态', '类'],
|
||||
languages: ['Java', 'C++', 'Python', 'Ruby']
|
||||
},
|
||||
@@ -167,13 +169,16 @@ const paradigms = [
|
||||
icon: 'λ',
|
||||
desc: '函数是核心',
|
||||
idea: '强调纯函数、不可变数据,避免副作用,关注"做什么"',
|
||||
example: '// 计算1-10的和\nconst sum = Array.from(\n {length: 10}, (_, i) => i + 1\n).reduce((a, b) => a + b, 0);',
|
||||
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))
|
||||
const currentParadigm = computed(() =>
|
||||
paradigms.find((p) => p.id === activeParadigm.value)
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -192,8 +197,14 @@ const currentParadigm = computed(() => paradigms.find(p => p.id === activeParadi
|
||||
margin-bottom: 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; }
|
||||
.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;
|
||||
@@ -281,7 +292,8 @@ const currentParadigm = computed(() => paradigms.find(p => p.id === activeParadi
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.detail-section {}
|
||||
.detail-section {
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-weight: 600;
|
||||
|
||||
+17
-7
@@ -11,8 +11,8 @@
|
||||
<div class="analogy-content">
|
||||
<div class="analogy-title">俄罗斯套娃</div>
|
||||
<div class="analogy-desc">
|
||||
打开一个大娃娃,里面有个小一点的娃娃<br>
|
||||
再打开还有更小的...直到最小的一个<br>
|
||||
打开一个大娃娃,里面有个小一点的娃娃<br />
|
||||
再打开还有更小的...直到最小的一个<br />
|
||||
<strong>这就是递归!</strong>
|
||||
</div>
|
||||
</div>
|
||||
@@ -101,7 +101,8 @@
|
||||
</div>
|
||||
<div class="dir-pseudocode">
|
||||
<div class="pseudo-title">伪代码</div>
|
||||
<pre>function traverse(folder) {
|
||||
<pre>
|
||||
function traverse(folder) {
|
||||
for each item in folder {
|
||||
if item is file {
|
||||
print(item)
|
||||
@@ -109,7 +110,8 @@
|
||||
traverse(item) // 递归调用!
|
||||
}
|
||||
}
|
||||
}</pre>
|
||||
}</pre
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -127,7 +129,9 @@
|
||||
<div class="element-card">
|
||||
<div class="element-number">2</div>
|
||||
<div class="element-title">递归调用</div>
|
||||
<div class="element-desc">如何让问题规模变小?调用自己处理更小的规模</div>
|
||||
<div class="element-desc">
|
||||
如何让问题规模变小?调用自己处理更小的规模
|
||||
</div>
|
||||
<div class="element-example">例:n! 转换成 (n-1)!</div>
|
||||
</div>
|
||||
<div class="element-card">
|
||||
@@ -189,8 +193,14 @@ const examples = [
|
||||
margin-bottom: 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; }
|
||||
.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;
|
||||
|
||||
@@ -10,29 +10,45 @@
|
||||
class="toggle-btn"
|
||||
:class="{ on: inputData === 1 }"
|
||||
@click="inputData = inputData === 1 ? 0 : 1"
|
||||
>{{ inputData }}</button>
|
||||
>
|
||||
{{ inputData }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Write -->
|
||||
<button class="write-btn" :class="{ flash: isWriting }" @click="writeOnce">
|
||||
<button
|
||||
class="write-btn"
|
||||
:class="{ flash: isWriting }"
|
||||
@click="writeOnce"
|
||||
>
|
||||
写入 →
|
||||
</button>
|
||||
|
||||
<!-- Stored -->
|
||||
<div class="reg-block">
|
||||
<span class="reg-title">存储</span>
|
||||
<span class="val-box" :class="{ on: storedData === 1, flash: isWriting }">{{ storedData }}</span>
|
||||
<span
|
||||
class="val-box"
|
||||
:class="{ on: storedData === 1, flash: isWriting }"
|
||||
>{{ storedData }}</span
|
||||
>
|
||||
</div>
|
||||
|
||||
<!-- Output -->
|
||||
<div class="reg-block">
|
||||
<span class="reg-title">输出</span>
|
||||
<span class="val-box out" :class="{ on: storedData === 1 }">{{ storedData }}</span>
|
||||
<span class="val-box out" :class="{ on: storedData === 1 }">{{
|
||||
storedData
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="status-line">
|
||||
{{ inputData !== storedData ? '⚡ 输入≠存储 → 点"写入"即可更新' : '✓ 输入与存储一致' }}
|
||||
{{
|
||||
inputData !== storedData
|
||||
? '⚡ 输入≠存储 → 点"写入"即可更新'
|
||||
: '✓ 输入与存储一致'
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -47,7 +63,9 @@ const isWriting = ref(false)
|
||||
const writeOnce = () => {
|
||||
isWriting.value = true
|
||||
storedData.value = inputData.value
|
||||
setTimeout(() => { isWriting.value = false }, 400)
|
||||
setTimeout(() => {
|
||||
isWriting.value = false
|
||||
}, 400)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
+54
-9
@@ -4,7 +4,8 @@
|
||||
|
||||
<div class="layers">
|
||||
<div
|
||||
v-for="(layer, i) in layers" :key="i"
|
||||
v-for="(layer, i) in layers"
|
||||
:key="i"
|
||||
class="layer-row"
|
||||
:class="{ active: activeLayer === i }"
|
||||
@mouseenter="activeLayer = i"
|
||||
@@ -23,7 +24,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="demo-caption">层层抽象封装,最底层的物理材料最终变成通用计算平台</div>
|
||||
<div class="demo-caption">
|
||||
层层抽象封装,最底层的物理材料最终变成通用计算平台
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -33,13 +36,55 @@ import { ref } from 'vue'
|
||||
const activeLayer = ref(null)
|
||||
|
||||
const layers = [
|
||||
{ icon: '🏖️', name: '沙子(硅)', desc: '地球上最丰富的元素之一,提炼出高纯度硅', scale: '原材料', arrow: '提纯 → 切割' },
|
||||
{ icon: '💿', name: '硅晶圆', desc: '直径约 30cm 的单晶硅片,表面极其光滑', scale: '基底', arrow: '光刻 → 蚀刻 → 掺杂' },
|
||||
{ icon: '🔌', name: '晶体管(开关)', desc: 'Gate=1 导通,Gate=0 断开,用电压控制电流', scale: '数百亿 / 芯片', arrow: '组合成逻辑电路' },
|
||||
{ icon: '🔲', name: '逻辑门', desc: 'AND / OR / NOT / XOR,实现基本布尔运算', scale: '数十亿', arrow: '组合成功能模块' },
|
||||
{ icon: '⚙️', name: '功能单元', desc: '加法器、寄存器、多路选择器……各司其职', scale: '数百个', arrow: '集成为处理器' },
|
||||
{ icon: '🧠', name: 'CPU 核心', desc: 'ALU + 控制器 + 寄存器组,取指→解码→执行→写回', scale: '1–128 核', arrow: '软件编程' },
|
||||
{ icon: '🚀', name: '软件应用', desc: '操作系统 / AI / 游戏 / 网页……一切皆指令', scale: '无限可能', arrow: '' },
|
||||
{
|
||||
icon: '🏖️',
|
||||
name: '沙子(硅)',
|
||||
desc: '地球上最丰富的元素之一,提炼出高纯度硅',
|
||||
scale: '原材料',
|
||||
arrow: '提纯 → 切割'
|
||||
},
|
||||
{
|
||||
icon: '💿',
|
||||
name: '硅晶圆',
|
||||
desc: '直径约 30cm 的单晶硅片,表面极其光滑',
|
||||
scale: '基底',
|
||||
arrow: '光刻 → 蚀刻 → 掺杂'
|
||||
},
|
||||
{
|
||||
icon: '🔌',
|
||||
name: '晶体管(开关)',
|
||||
desc: 'Gate=1 导通,Gate=0 断开,用电压控制电流',
|
||||
scale: '数百亿 / 芯片',
|
||||
arrow: '组合成逻辑电路'
|
||||
},
|
||||
{
|
||||
icon: '🔲',
|
||||
name: '逻辑门',
|
||||
desc: 'AND / OR / NOT / XOR,实现基本布尔运算',
|
||||
scale: '数十亿',
|
||||
arrow: '组合成功能模块'
|
||||
},
|
||||
{
|
||||
icon: '⚙️',
|
||||
name: '功能单元',
|
||||
desc: '加法器、寄存器、多路选择器……各司其职',
|
||||
scale: '数百个',
|
||||
arrow: '集成为处理器'
|
||||
},
|
||||
{
|
||||
icon: '🧠',
|
||||
name: 'CPU 核心',
|
||||
desc: 'ALU + 控制器 + 寄存器组,取指→解码→执行→写回',
|
||||
scale: '1–128 核',
|
||||
arrow: '软件编程'
|
||||
},
|
||||
{
|
||||
icon: '🚀',
|
||||
name: '软件应用',
|
||||
desc: '操作系统 / AI / 游戏 / 网页……一切皆指令',
|
||||
scale: '无限可能',
|
||||
arrow: ''
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
|
||||
+39
-14
@@ -28,17 +28,29 @@
|
||||
<div
|
||||
v-for="(num, index) in numbers"
|
||||
:key="index"
|
||||
:class="['array-cell', { found: index === foundIndex, searching: index <= searchStep && searching }]"
|
||||
: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="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" />
|
||||
目标数字:<input
|
||||
v-model="targetNumber"
|
||||
type="number"
|
||||
class="target-input"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="algo-stats">
|
||||
@@ -55,19 +67,26 @@
|
||||
<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
|
||||
}]"
|
||||
: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-step">
|
||||
查找范围:[{{ binaryLeft }}, {{ binaryRight }}]
|
||||
</div>
|
||||
<div class="info-mid">中间位置:{{ binaryMid }}</div>
|
||||
<div class="info-comparison">{{ sortedNumbers[binaryMid] }} vs {{ binaryTarget }}</div>
|
||||
<div class="info-comparison">
|
||||
{{ sortedNumbers[binaryMid] }} vs {{ binaryTarget }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="search-controls">
|
||||
<button @click="binaryStep" class="search-btn">下一步</button>
|
||||
@@ -125,7 +144,7 @@ const startLinearSearch = () => {
|
||||
searching.value = true
|
||||
searchStep.value = -1
|
||||
foundIndex.value = -1
|
||||
|
||||
|
||||
let step = 0
|
||||
const interval = setInterval(() => {
|
||||
if (step < numbers.value.length) {
|
||||
@@ -151,7 +170,7 @@ const reset = () => {
|
||||
|
||||
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) {
|
||||
@@ -185,8 +204,14 @@ const resetBinary = () => {
|
||||
margin-bottom: 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; }
|
||||
.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;
|
||||
|
||||
+24
-15
@@ -10,7 +10,7 @@
|
||||
v-for="(item, index) in array"
|
||||
:key="index"
|
||||
class="array-bar"
|
||||
:class="{
|
||||
:class="{
|
||||
comparing: comparingIndices.includes(index),
|
||||
swapping: swappingIndices.includes(index),
|
||||
sorted: index < sortedCount
|
||||
@@ -92,27 +92,30 @@ const currentAlgoDesc = ref('选择一个排序算法开始演示')
|
||||
const complexity = ref('')
|
||||
|
||||
const generateArray = () => {
|
||||
array.value = Array.from({ length: 10 }, () => Math.floor(Math.random() * 90) + 10)
|
||||
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 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)
|
||||
@@ -124,7 +127,7 @@ const startBubbleSort = async () => {
|
||||
}
|
||||
sortedCount.value++
|
||||
}
|
||||
|
||||
|
||||
comparingIndices.value = []
|
||||
sortedCount.value = arr.length
|
||||
}
|
||||
@@ -133,10 +136,10 @@ 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
|
||||
@@ -154,11 +157,11 @@ const quickSort = async (arr, low, 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]
|
||||
@@ -168,14 +171,14 @@ const partition = async (arr, low, high) => {
|
||||
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>
|
||||
@@ -196,8 +199,14 @@ const partition = async (arr, low, high) => {
|
||||
margin-bottom: 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; }
|
||||
.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;
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
|
||||
<div class="demo-content">
|
||||
<div class="storage-pyramid">
|
||||
<div
|
||||
v-for="(level, i) in storageLevels"
|
||||
<div
|
||||
v-for="(level, i) in storageLevels"
|
||||
:key="level.name"
|
||||
class="level"
|
||||
:class="{ active: activeLevel === i }"
|
||||
@@ -27,13 +27,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="currentLevel"
|
||||
class="level-detail"
|
||||
>
|
||||
<div class="detail-title">
|
||||
{{ currentLevel.name }} 详情
|
||||
</div>
|
||||
<div v-if="currentLevel" class="level-detail">
|
||||
<div class="detail-title">{{ currentLevel.name }} 详情</div>
|
||||
<div class="detail-grid">
|
||||
<div class="detail-item">
|
||||
<span class="label">访问速度</span>
|
||||
@@ -59,7 +54,9 @@
|
||||
</div>
|
||||
|
||||
<div class="info-box">
|
||||
<strong>核心思想:</strong>存储遵循"金字塔"原则:越快的存储越贵、容量越小。CPU 需要的数据放在最快的存储(寄存器、缓存),暂时不用的放在慢速大容量存储(磁盘、云端)。
|
||||
<strong>核心思想:</strong
|
||||
>存储遵循"金字塔"原则:越快的存储越贵、容量越小。CPU
|
||||
需要的数据放在最快的存储(寄存器、缓存),暂时不用的放在慢速大容量存储(磁盘、云端)。
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -70,55 +67,55 @@ import { ref, computed } from 'vue'
|
||||
const activeLevel = ref(0)
|
||||
|
||||
const storageLevels = [
|
||||
{
|
||||
name: '寄存器',
|
||||
speed: '~1 纳秒',
|
||||
size: '几百字节',
|
||||
{
|
||||
name: '寄存器',
|
||||
speed: '~1 纳秒',
|
||||
size: '几百字节',
|
||||
width: '30%',
|
||||
cost: '极高',
|
||||
volatile: '是',
|
||||
desc: 'CPU 内部最快的存储,直接参与运算。数量有限,由编译器自动管理。'
|
||||
},
|
||||
{
|
||||
name: 'L1 缓存',
|
||||
speed: '~2 纳秒',
|
||||
size: '32-64 KB',
|
||||
{
|
||||
name: 'L1 缓存',
|
||||
speed: '~2 纳秒',
|
||||
size: '32-64 KB',
|
||||
width: '45%',
|
||||
cost: '很高',
|
||||
volatile: '是',
|
||||
desc: 'CPU 内置的高速缓存,存储最常用的数据。每个核心独立拥有。'
|
||||
},
|
||||
{
|
||||
name: 'L2/L3 缓存',
|
||||
speed: '~10 纳秒',
|
||||
size: '几 MB',
|
||||
{
|
||||
name: 'L2/L3 缓存',
|
||||
speed: '~10 纳秒',
|
||||
size: '几 MB',
|
||||
width: '60%',
|
||||
cost: '高',
|
||||
volatile: '是',
|
||||
desc: '更大但稍慢的缓存,L3 通常多核心共享。'
|
||||
},
|
||||
{
|
||||
name: '内存 (RAM)',
|
||||
speed: '~100 纳秒',
|
||||
size: '8-128 GB',
|
||||
{
|
||||
name: '内存 (RAM)',
|
||||
speed: '~100 纳秒',
|
||||
size: '8-128 GB',
|
||||
width: '75%',
|
||||
cost: '中等',
|
||||
volatile: '是',
|
||||
desc: '程序运行时的主要工作区。断电后数据丢失。'
|
||||
},
|
||||
{
|
||||
name: 'SSD 固态硬盘',
|
||||
speed: '~100 微秒',
|
||||
size: '256 GB - 4 TB',
|
||||
{
|
||||
name: 'SSD 固态硬盘',
|
||||
speed: '~100 微秒',
|
||||
size: '256 GB - 4 TB',
|
||||
width: '90%',
|
||||
cost: '较低',
|
||||
volatile: '否',
|
||||
desc: '比机械硬盘快很多,无机械部件。断电数据保留。'
|
||||
},
|
||||
{
|
||||
name: 'HDD 机械硬盘',
|
||||
speed: '~10 毫秒',
|
||||
size: '1-20 TB',
|
||||
{
|
||||
name: 'HDD 机械硬盘',
|
||||
speed: '~10 毫秒',
|
||||
size: '1-20 TB',
|
||||
width: '100%',
|
||||
cost: '低',
|
||||
volatile: '否',
|
||||
@@ -145,8 +142,15 @@ const currentLevel = computed(() => storageLevels[activeLevel.value])
|
||||
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;
|
||||
}
|
||||
|
||||
.demo-content {
|
||||
display: flex;
|
||||
@@ -251,5 +255,4 @@ const currentLevel = computed(() => storageLevels[activeLevel.value])
|
||||
display: flex;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
+9
-4
@@ -106,8 +106,7 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
</script>
|
||||
<script setup></script>
|
||||
|
||||
<style scoped>
|
||||
.storage-hierarchy-demo {
|
||||
@@ -125,8 +124,14 @@
|
||||
margin-bottom: 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; }
|
||||
.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;
|
||||
|
||||
+45
-31
@@ -16,7 +16,7 @@
|
||||
min="0"
|
||||
max="255"
|
||||
@input="calculate"
|
||||
>
|
||||
/>
|
||||
<span>.</span>
|
||||
<input
|
||||
v-model="ip[1]"
|
||||
@@ -24,7 +24,7 @@
|
||||
min="0"
|
||||
max="255"
|
||||
@input="calculate"
|
||||
>
|
||||
/>
|
||||
<span>.</span>
|
||||
<input
|
||||
v-model="ip[2]"
|
||||
@@ -32,7 +32,7 @@
|
||||
min="0"
|
||||
max="255"
|
||||
@input="calculate"
|
||||
>
|
||||
/>
|
||||
<span>.</span>
|
||||
<input
|
||||
v-model="ip[3]"
|
||||
@@ -40,7 +40,7 @@
|
||||
min="0"
|
||||
max="255"
|
||||
@input="calculate"
|
||||
>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
@@ -53,7 +53,7 @@
|
||||
min="8"
|
||||
max="30"
|
||||
@input="calculate"
|
||||
>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -82,9 +82,7 @@
|
||||
</div>
|
||||
|
||||
<div class="binary-section">
|
||||
<div class="binary-title">
|
||||
二进制表示
|
||||
</div>
|
||||
<div class="binary-title">二进制表示</div>
|
||||
<div class="binary-row">
|
||||
<span class="binary-label">IP 地址:</span>
|
||||
<span class="binary-value">{{ ipBinary }}</span>
|
||||
@@ -104,13 +102,11 @@
|
||||
</div>
|
||||
|
||||
<div class="visual-section">
|
||||
<div class="visual-title">
|
||||
地址结构可视化
|
||||
</div>
|
||||
<div class="visual-title">地址结构可视化</div>
|
||||
<div class="address-visual">
|
||||
<div class="bit-blocks">
|
||||
<div
|
||||
v-for="(bit, i) in bits"
|
||||
<div
|
||||
v-for="(bit, i) in bits"
|
||||
:key="i"
|
||||
:class="['bit', { network: i < cidr, host: i >= cidr }]"
|
||||
>
|
||||
@@ -118,15 +114,21 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="legend">
|
||||
<span class="legend-item"><span class="network-box" /> 网络位 ({{ cidr }}位)</span>
|
||||
<span class="legend-item"><span class="host-box" /> 主机位 ({{ 32 - cidr }}位)</span>
|
||||
<span class="legend-item"
|
||||
><span class="network-box" /> 网络位 ({{ cidr }}位)</span
|
||||
>
|
||||
<span class="legend-item"
|
||||
><span class="host-box" /> 主机位 ({{ 32 - cidr }}位)</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-box">
|
||||
<strong>核心思想:</strong>子网掩码决定了 IP 地址的哪部分是"网络号"(小区),哪部分是"主机号"(房间)。/24 表示前 24 位是网络位,后 8 位是主机位。
|
||||
<strong>核心思想:</strong>子网掩码决定了 IP
|
||||
地址的哪部分是"网络号"(小区),哪部分是"主机号"(房间)。/24 表示前 24
|
||||
位是网络位,后 8 位是主机位。
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -138,7 +140,7 @@ const ip = ref([192, 168, 1, 100])
|
||||
const cidr = ref(24)
|
||||
|
||||
const mask = computed(() => {
|
||||
const maskValue = 0xFFFFFFFF << (32 - cidr.value)
|
||||
const maskValue = 0xffffffff << (32 - cidr.value)
|
||||
return [
|
||||
(maskValue >>> 24) & 255,
|
||||
(maskValue >>> 16) & 255,
|
||||
@@ -148,14 +150,16 @@ const mask = computed(() => {
|
||||
})
|
||||
|
||||
const ipValue = computed(() => {
|
||||
return (parseInt(ip.value[0]) << 24) +
|
||||
(parseInt(ip.value[1]) << 16) +
|
||||
(parseInt(ip.value[2]) << 8) +
|
||||
parseInt(ip.value[3])
|
||||
return (
|
||||
(parseInt(ip.value[0]) << 24) +
|
||||
(parseInt(ip.value[1]) << 16) +
|
||||
(parseInt(ip.value[2]) << 8) +
|
||||
parseInt(ip.value[3])
|
||||
)
|
||||
})
|
||||
|
||||
const maskValue = computed(() => {
|
||||
return 0xFFFFFFFF << (32 - cidr.value)
|
||||
return 0xffffffff << (32 - cidr.value)
|
||||
})
|
||||
|
||||
const networkAddress = computed(() => {
|
||||
@@ -187,21 +191,21 @@ const hostRange = computed(() => {
|
||||
const network = ipValue.value & maskValue.value
|
||||
const first = network + 1
|
||||
const last = (network | (~maskValue.value >>> 0)) - 1
|
||||
|
||||
|
||||
const firstIP = [
|
||||
(first >>> 24) & 255,
|
||||
(first >>> 16) & 255,
|
||||
(first >>> 8) & 255,
|
||||
first & 255
|
||||
].join('.')
|
||||
|
||||
|
||||
const lastIP = [
|
||||
(last >>> 24) & 255,
|
||||
(last >>> 16) & 255,
|
||||
(last >>> 8) & 255,
|
||||
last & 255
|
||||
].join('.')
|
||||
|
||||
|
||||
return `${firstIP} - ${lastIP}`
|
||||
})
|
||||
|
||||
@@ -224,7 +228,10 @@ const maskBinary = computed(() => {
|
||||
})
|
||||
|
||||
const bits = computed(() => {
|
||||
return ip.value.map(octet => toBinary(parseInt(octet))).join('').split('')
|
||||
return ip.value
|
||||
.map((octet) => toBinary(parseInt(octet)))
|
||||
.join('')
|
||||
.split('')
|
||||
})
|
||||
|
||||
const networkBinary = computed(() => {
|
||||
@@ -236,7 +243,7 @@ const hostBinary = computed(() => {
|
||||
})
|
||||
|
||||
const calculate = () => {
|
||||
ip.value = ip.value.map(v => Math.min(255, Math.max(0, parseInt(v) || 0)))
|
||||
ip.value = ip.value.map((v) => Math.min(255, Math.max(0, parseInt(v) || 0)))
|
||||
cidr.value = Math.min(30, Math.max(8, cidr.value || 24))
|
||||
}
|
||||
|
||||
@@ -261,8 +268,15 @@ onMounted(() => {
|
||||
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;
|
||||
}
|
||||
|
||||
.input-section {
|
||||
display: flex;
|
||||
@@ -434,7 +448,8 @@ onMounted(() => {
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.network-box, .host-box {
|
||||
.network-box,
|
||||
.host-box {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 2px;
|
||||
@@ -458,5 +473,4 @@ onMounted(() => {
|
||||
display: flex;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
+76
-85
@@ -7,14 +7,14 @@
|
||||
|
||||
<div class="demo-content">
|
||||
<div class="comparison-tabs">
|
||||
<button
|
||||
<button
|
||||
:class="['tab-btn', { active: activeTab === 'tcp' }]"
|
||||
@click="activeTab = 'tcp'"
|
||||
>
|
||||
<span class="tab-icon">📨</span>
|
||||
<span>TCP (可靠)</span>
|
||||
</button>
|
||||
<button
|
||||
<button
|
||||
:class="['tab-btn', { active: activeTab === 'udp' }]"
|
||||
@click="activeTab = 'udp'"
|
||||
>
|
||||
@@ -23,15 +23,12 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="currentProtocol"
|
||||
class="protocol-detail"
|
||||
>
|
||||
<div v-if="currentProtocol" class="protocol-detail">
|
||||
<div class="detail-header">
|
||||
<span class="detail-name">{{ currentProtocol.name }}</span>
|
||||
<span class="detail-full">{{ currentProtocol.fullName }}</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="feature-grid">
|
||||
<div
|
||||
v-for="(feature, i) in currentProtocol.features"
|
||||
@@ -45,9 +42,7 @@
|
||||
</div>
|
||||
|
||||
<div class="mechanism-section">
|
||||
<div class="mechanism-title">
|
||||
核心机制
|
||||
</div>
|
||||
<div class="mechanism-title">核心机制</div>
|
||||
<div class="mechanism-list">
|
||||
<div
|
||||
v-for="(m, i) in currentProtocol.mechanisms"
|
||||
@@ -61,70 +56,53 @@
|
||||
</div>
|
||||
|
||||
<div class="use-cases">
|
||||
<div class="use-title">
|
||||
适用场景
|
||||
</div>
|
||||
<div class="use-title">适用场景</div>
|
||||
<div class="use-tags">
|
||||
<span
|
||||
v-for="(use, i) in currentProtocol.useCases"
|
||||
:key="i"
|
||||
class="use-tag"
|
||||
>{{ use }}</span>
|
||||
>{{ use }}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="visual-demo">
|
||||
<div class="visual-title">
|
||||
传输过程演示
|
||||
</div>
|
||||
<div class="visual-title">传输过程演示</div>
|
||||
<div class="transmission-demo">
|
||||
<div class="sender">
|
||||
<div class="node-label">
|
||||
发送方
|
||||
</div>
|
||||
<div class="node-label">发送方</div>
|
||||
<div class="packets">
|
||||
<div
|
||||
v-for="(packet, i) in packets"
|
||||
<div
|
||||
v-for="(packet, i) in packets"
|
||||
:key="i"
|
||||
:class="['packet', { sent: packet.sent, acked: packet.acked, lost: packet.lost }]"
|
||||
:class="[
|
||||
'packet',
|
||||
{ sent: packet.sent, acked: packet.acked, lost: packet.lost }
|
||||
]"
|
||||
>
|
||||
{{ packet.seq }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="network-channel">
|
||||
<div class="channel-label">
|
||||
网络通道
|
||||
</div>
|
||||
<div
|
||||
class="channel-status"
|
||||
:class="{ congested: isCongested }"
|
||||
>
|
||||
<div class="channel-label">网络通道</div>
|
||||
<div class="channel-status" :class="{ congested: isCongested }">
|
||||
{{ isCongested ? '拥堵' : '正常' }}
|
||||
</div>
|
||||
<button
|
||||
class="demo-btn"
|
||||
@click="runDemo"
|
||||
>
|
||||
开始演示
|
||||
</button>
|
||||
<button
|
||||
class="demo-btn"
|
||||
@click="toggleCongestion"
|
||||
>
|
||||
<button class="demo-btn" @click="runDemo">开始演示</button>
|
||||
<button class="demo-btn" @click="toggleCongestion">
|
||||
{{ isCongested ? '恢复网络' : '模拟丢包' }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="receiver">
|
||||
<div class="node-label">
|
||||
接收方
|
||||
</div>
|
||||
<div class="node-label">接收方</div>
|
||||
<div class="received-packets">
|
||||
<div
|
||||
v-for="(packet, i) in receivedPackets"
|
||||
<div
|
||||
v-for="(packet, i) in receivedPackets"
|
||||
:key="i"
|
||||
class="received-packet"
|
||||
>
|
||||
@@ -133,17 +111,11 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="demo-log">
|
||||
<div class="log-title">
|
||||
传输日志
|
||||
</div>
|
||||
<div class="log-title">传输日志</div>
|
||||
<div class="log-content">
|
||||
<div
|
||||
v-for="(log, i) in logs"
|
||||
:key="i"
|
||||
class="log-item"
|
||||
>
|
||||
<div v-for="(log, i) in logs" :key="i" class="log-item">
|
||||
{{ log }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -151,9 +123,7 @@
|
||||
</div>
|
||||
|
||||
<div class="comparison-table">
|
||||
<div class="table-title">
|
||||
特性对比
|
||||
</div>
|
||||
<div class="table-title">特性对比</div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -163,10 +133,7 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="(row, i) in comparisonData"
|
||||
:key="i"
|
||||
>
|
||||
<tr v-for="(row, i) in comparisonData" :key="i">
|
||||
<td class="feature-col">
|
||||
{{ row.feature }}
|
||||
</td>
|
||||
@@ -183,7 +150,9 @@
|
||||
</div>
|
||||
|
||||
<div class="info-box">
|
||||
<strong>核心思想:</strong>TCP 像挂号信,确保送达但较慢;UDP 像平信,快速但不保证送达。选择哪种协议取决于应用场景:需要可靠性选 TCP,需要实时性选 UDP。
|
||||
<strong>核心思想:</strong>TCP 像挂号信,确保送达但较慢;UDP
|
||||
像平信,快速但不保证送达。选择哪种协议取决于应用场景:需要可靠性选
|
||||
TCP,需要实时性选 UDP。
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -267,25 +236,25 @@ const toggleCongestion = () => {
|
||||
const runDemo = async () => {
|
||||
receivedPackets.value = []
|
||||
logs.value = ['开始传输演示...']
|
||||
|
||||
|
||||
for (let i = 0; i < packets.value.length; i++) {
|
||||
packets.value[i].sent = false
|
||||
packets.value[i].acked = false
|
||||
packets.value[i].lost = false
|
||||
}
|
||||
|
||||
|
||||
const isTcp = activeTab.value === 'tcp'
|
||||
|
||||
|
||||
for (let i = 0; i < packets.value.length; i++) {
|
||||
const packet = packets.value[i]
|
||||
packet.sent = true
|
||||
|
||||
|
||||
if (isCongested.value && Math.random() > 0.5) {
|
||||
packet.lost = true
|
||||
logs.value.push(`包 ${packet.seq} 丢失!`)
|
||||
|
||||
|
||||
if (isTcp) {
|
||||
await new Promise(r => setTimeout(r, 500))
|
||||
await new Promise((r) => setTimeout(r, 500))
|
||||
logs.value.push(`TCP 重传包 ${packet.seq}...`)
|
||||
packet.lost = false
|
||||
receivedPackets.value.push(packet.seq)
|
||||
@@ -297,12 +266,14 @@ const runDemo = async () => {
|
||||
packet.acked = true
|
||||
logs.value.push(`包 ${packet.seq} 送达`)
|
||||
}
|
||||
|
||||
await new Promise(r => setTimeout(r, 300))
|
||||
|
||||
await new Promise((r) => setTimeout(r, 300))
|
||||
}
|
||||
|
||||
|
||||
if (isTcp) {
|
||||
logs.value.push(`TCP 完成: 收到 ${receivedPackets.value.length} 个包,顺序: ${receivedPackets.value.join(', ')}`)
|
||||
logs.value.push(
|
||||
`TCP 完成: 收到 ${receivedPackets.value.length} 个包,顺序: ${receivedPackets.value.join(', ')}`
|
||||
)
|
||||
} else {
|
||||
logs.value.push(`UDP 完成: 收到 ${receivedPackets.value.length} 个包`)
|
||||
}
|
||||
@@ -325,8 +296,15 @@ const runDemo = async () => {
|
||||
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;
|
||||
}
|
||||
|
||||
.comparison-tabs {
|
||||
display: flex;
|
||||
@@ -353,7 +331,9 @@ const runDemo = async () => {
|
||||
background: var(--vp-c-brand-soft);
|
||||
}
|
||||
|
||||
.tab-icon { font-size: 1.1rem; }
|
||||
.tab-icon {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.protocol-detail {
|
||||
background: var(--vp-c-bg);
|
||||
@@ -395,15 +375,24 @@ const runDemo = async () => {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.feature-icon { font-size: 1rem; }
|
||||
.feature-name { font-size: 0.75rem; color: var(--vp-c-text-2); }
|
||||
.feature-value { font-size: 0.8rem; font-weight: bold; }
|
||||
.feature-icon {
|
||||
font-size: 1rem;
|
||||
}
|
||||
.feature-name {
|
||||
font-size: 0.75rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
.feature-value {
|
||||
font-size: 0.8rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.mechanism-section {
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.mechanism-title, .use-title {
|
||||
.mechanism-title,
|
||||
.use-title {
|
||||
font-weight: bold;
|
||||
font-size: 0.85rem;
|
||||
margin-bottom: 0.5rem;
|
||||
@@ -465,7 +454,8 @@ const runDemo = async () => {
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.sender, .receiver {
|
||||
.sender,
|
||||
.receiver {
|
||||
flex: 1;
|
||||
padding: 0.5rem;
|
||||
background: var(--vp-c-bg-alt);
|
||||
@@ -478,7 +468,8 @@ const runDemo = async () => {
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.packets, .received-packets {
|
||||
.packets,
|
||||
.received-packets {
|
||||
display: flex;
|
||||
gap: 0.25rem;
|
||||
flex-wrap: wrap;
|
||||
@@ -604,7 +595,8 @@ table {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
th, td {
|
||||
th,
|
||||
td {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
padding: 0.4rem;
|
||||
text-align: center;
|
||||
@@ -635,5 +627,4 @@ th {
|
||||
display: flex;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
+31
-11
@@ -5,7 +5,7 @@
|
||||
<div class="schematic" @click="gateOn = !gateOn">
|
||||
<!-- Source terminal -->
|
||||
<div class="terminal-box source">
|
||||
<span class="pin-label">源极<br><span class="en">Source</span></span>
|
||||
<span class="pin-label">源极<br /><span class="en">Source</span></span>
|
||||
<div class="pin-wire" :class="{ active: gateOn }"></div>
|
||||
</div>
|
||||
|
||||
@@ -23,13 +23,15 @@
|
||||
</template>
|
||||
<span v-else class="block-mark">✕</span>
|
||||
</div>
|
||||
<div class="channel-status">{{ gateOn ? '导通 → 输出 1' : '断开 → 输出 0' }}</div>
|
||||
<div class="channel-status">
|
||||
{{ gateOn ? '导通 → 输出 1' : '断开 → 输出 0' }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Drain terminal -->
|
||||
<div class="terminal-box drain">
|
||||
<div class="pin-wire" :class="{ active: gateOn }"></div>
|
||||
<span class="pin-label">漏极<br><span class="en">Drain</span></span>
|
||||
<span class="pin-label">漏极<br /><span class="en">Drain</span></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -179,14 +181,28 @@ const gateOn = ref(false)
|
||||
animation: flow 1.2s linear infinite;
|
||||
}
|
||||
|
||||
.electron.e2 { animation-delay: 0.4s; }
|
||||
.electron.e3 { animation-delay: 0.8s; }
|
||||
.electron.e2 {
|
||||
animation-delay: 0.4s;
|
||||
}
|
||||
.electron.e3 {
|
||||
animation-delay: 0.8s;
|
||||
}
|
||||
|
||||
@keyframes flow {
|
||||
0% { left: -8%; opacity: 0; }
|
||||
10% { opacity: 1; }
|
||||
90% { opacity: 1; }
|
||||
100% { left: 108%; opacity: 0; }
|
||||
0% {
|
||||
left: -8%;
|
||||
opacity: 0;
|
||||
}
|
||||
10% {
|
||||
opacity: 1;
|
||||
}
|
||||
90% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
left: 108%;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.channel-status {
|
||||
@@ -208,7 +224,11 @@ const gateOn = ref(false)
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.pin-wire { width: 1.5rem; }
|
||||
.channel-area { min-width: 5rem; }
|
||||
.pin-wire {
|
||||
width: 1.5rem;
|
||||
}
|
||||
.channel-area {
|
||||
min-width: 5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
+42
-72
@@ -12,36 +12,20 @@
|
||||
:class="{ active: activeType === 'serial' }"
|
||||
@click="activeType = 'serial'"
|
||||
>
|
||||
<div class="card-icon">
|
||||
➡️
|
||||
</div>
|
||||
<div class="card-title">
|
||||
串行传输
|
||||
</div>
|
||||
<div class="card-desc">
|
||||
一位一位依次传输
|
||||
</div>
|
||||
<div class="card-examples">
|
||||
USB、SATA、PCIe
|
||||
</div>
|
||||
<div class="card-icon">➡️</div>
|
||||
<div class="card-title">串行传输</div>
|
||||
<div class="card-desc">一位一位依次传输</div>
|
||||
<div class="card-examples">USB、SATA、PCIe</div>
|
||||
</div>
|
||||
<div
|
||||
class="type-card"
|
||||
:class="{ active: activeType === 'parallel' }"
|
||||
@click="activeType = 'parallel'"
|
||||
>
|
||||
<div class="card-icon">
|
||||
⬇️⬇️⬇️⬇️
|
||||
</div>
|
||||
<div class="card-title">
|
||||
并行传输
|
||||
</div>
|
||||
<div class="card-desc">
|
||||
多位同时传输
|
||||
</div>
|
||||
<div class="card-examples">
|
||||
旧式打印机接口、IDE
|
||||
</div>
|
||||
<div class="card-icon">⬇️⬇️⬇️⬇️</div>
|
||||
<div class="card-title">并行传输</div>
|
||||
<div class="card-desc">多位同时传输</div>
|
||||
<div class="card-examples">旧式打印机接口、IDE</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -51,47 +35,35 @@
|
||||
</div>
|
||||
<div class="visual-area">
|
||||
<div class="sender">
|
||||
<div class="device-label">
|
||||
发送端
|
||||
</div>
|
||||
<div class="device-label">发送端</div>
|
||||
<div class="data-bits">
|
||||
<span
|
||||
v-for="(bit, i) in dataBits"
|
||||
:key="i"
|
||||
class="bit"
|
||||
:class="{ sending: sendingBit === i && activeType === 'serial' }"
|
||||
>{{ bit }}</span>
|
||||
:class="{
|
||||
sending: sendingBit === i && activeType === 'serial'
|
||||
}"
|
||||
>{{ bit }}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="channels">
|
||||
<div
|
||||
v-if="activeType === 'serial'"
|
||||
class="channel serial"
|
||||
>
|
||||
<div class="channel-label">
|
||||
单通道
|
||||
</div>
|
||||
<div v-if="activeType === 'serial'" class="channel serial">
|
||||
<div class="channel-label">单通道</div>
|
||||
<div class="channel-flow">
|
||||
<span
|
||||
v-for="i in 5"
|
||||
:key="i"
|
||||
class="flow-dot"
|
||||
:class="{ active: sendingBit !== null }"
|
||||
>●</span>
|
||||
>●</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="channel parallel"
|
||||
>
|
||||
<div
|
||||
v-for="i in 4"
|
||||
:key="i"
|
||||
class="channel-row"
|
||||
>
|
||||
<div class="channel-label">
|
||||
通道{{ i }}
|
||||
</div>
|
||||
<div v-else class="channel parallel">
|
||||
<div v-for="i in 4" :key="i" class="channel-row">
|
||||
<div class="channel-label">通道{{ i }}</div>
|
||||
<div class="channel-flow">
|
||||
<span class="flow-dot active">●</span>
|
||||
</div>
|
||||
@@ -99,30 +71,19 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="receiver">
|
||||
<div class="device-label">
|
||||
接收端
|
||||
</div>
|
||||
<div class="device-label">接收端</div>
|
||||
<div class="data-bits received">
|
||||
<span
|
||||
v-for="(bit, i) in receivedBits"
|
||||
:key="i"
|
||||
class="bit"
|
||||
>{{ bit }}</span>
|
||||
<span v-for="(bit, i) in receivedBits" :key="i" class="bit">{{
|
||||
bit
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
class="send-btn"
|
||||
@click="startTransmission"
|
||||
>
|
||||
发送数据
|
||||
</button>
|
||||
<button class="send-btn" @click="startTransmission">发送数据</button>
|
||||
</div>
|
||||
|
||||
<div class="comparison-table">
|
||||
<div class="table-title">
|
||||
串行 vs 并行对比
|
||||
</div>
|
||||
<div class="table-title">串行 vs 并行对比</div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -158,7 +119,8 @@
|
||||
</div>
|
||||
|
||||
<div class="info-box">
|
||||
<strong>核心思想:</strong>现代高速传输多采用串行方式。虽然并行"看起来"更快(一次传多位),但串行可以跑更高频率,抗干扰更强,实际速度反而更快。
|
||||
<strong>核心思想:</strong
|
||||
>现代高速传输多采用串行方式。虽然并行"看起来"更快(一次传多位),但串行可以跑更高频率,抗干扰更强,实际速度反而更快。
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -207,8 +169,15 @@ const startTransmission = () => {
|
||||
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;
|
||||
}
|
||||
|
||||
.demo-content {
|
||||
display: flex;
|
||||
@@ -279,7 +248,8 @@ const startTransmission = () => {
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.sender, .receiver {
|
||||
.sender,
|
||||
.receiver {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@@ -382,7 +352,8 @@ table {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
th, td {
|
||||
th,
|
||||
td {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
padding: 0.4rem;
|
||||
text-align: center;
|
||||
@@ -402,5 +373,4 @@ th {
|
||||
display: flex;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
+21
-6
@@ -107,7 +107,8 @@
|
||||
<div class="port-title">端口号:应用程序的标识</div>
|
||||
<div class="port-examples">
|
||||
<div class="port-intro">
|
||||
端口号就像公寓房间号,IP 地址是公寓楼地址,合起来才能找到具体的应用程序
|
||||
端口号就像公寓房间号,IP
|
||||
地址是公寓楼地址,合起来才能找到具体的应用程序
|
||||
</div>
|
||||
<div class="port-list">
|
||||
<div
|
||||
@@ -222,8 +223,14 @@ const currentProtocol = computed(() => protocolData[activeProtocol.value])
|
||||
margin-bottom: 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; }
|
||||
.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;
|
||||
@@ -422,9 +429,17 @@ const currentProtocol = computed(() => protocolData[activeProtocol.value])
|
||||
}
|
||||
|
||||
@keyframes slideRight {
|
||||
0% { transform: translateX(-100%); opacity: 0; }
|
||||
50% { opacity: 1; }
|
||||
100% { transform: translateX(100%); opacity: 0; }
|
||||
0% {
|
||||
transform: translateX(-100%);
|
||||
opacity: 0;
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: translateX(100%);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.side-desc {
|
||||
|
||||
+42
-18
@@ -24,20 +24,43 @@
|
||||
<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"
|
||||
<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)` }"
|
||||
<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>
|
||||
<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>
|
||||
@@ -83,14 +106,9 @@
|
||||
<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>
|
||||
<html> <body> <div class="container">
|
||||
<h1>标题</h1> <p>段落</p> </div>
|
||||
</body> </html>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dom-structure">
|
||||
@@ -235,8 +253,14 @@ const binaryTreeLines = [
|
||||
margin-bottom: 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; }
|
||||
.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;
|
||||
|
||||
Reference in New Issue
Block a user