feat: 添加多个附录交互式组件和文档更新
- 添加浏览器前端组件:无障碍访问、国际化、实时通信 - 添加 Transformer 注意力机制系列组件 - 更新 Canvas、数据追踪等现有组件 - 修复 ESLint 变量名冲突问题 - 完善相关附录文档
This commit is contained in:
@@ -1,189 +1,308 @@
|
||||
<template>
|
||||
<div class="dns-lookup-demo simple-mode">
|
||||
<div class="concept-explanation">
|
||||
<p class="why-text">
|
||||
<strong>为什么需要 DNS?(查导航)</strong>
|
||||
</p>
|
||||
<p class="why-desc-zh">
|
||||
你知道店铺名字叫 "bilibili.com",但快递员需要知道具体的经纬度坐标 (IP 地址)
|
||||
才能送达。
|
||||
<br>
|
||||
DNS 就像是<strong>地图导航</strong>,输入店名,它通过“114查号台”帮你找到坐标。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="demo-stage">
|
||||
<div class="input-area">
|
||||
<span class="label">店铺名称 (域名)</span>
|
||||
<div class="fake-input">
|
||||
bilibili.com
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="process-animation">
|
||||
<div class="arrow-down">
|
||||
⬇️
|
||||
</div>
|
||||
<div class="dns-box">
|
||||
<div class="icon">
|
||||
🧭
|
||||
</div>
|
||||
<div class="title">
|
||||
DNS (查号台)
|
||||
</div>
|
||||
<div class="desc">
|
||||
正在查询 bilibili.com 的 IP...
|
||||
</div>
|
||||
</div>
|
||||
<div class="arrow-down">
|
||||
⬇️
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="output-area">
|
||||
<span class="label">精准坐标 (IP 地址)</span>
|
||||
<div class="fake-output">
|
||||
110.43.12.55
|
||||
</div>
|
||||
</div>
|
||||
<div class="dns-lookup-demo custom-demo-base">
|
||||
<div class="demo-label">DNS 解析 ── 查地址簿找坐标</div>
|
||||
<div class="demo-panel">
|
||||
|
||||
<div class="lookup-flow">
|
||||
<!-- 浏览器 -->
|
||||
<div class="flow-node browser-node" :class="{ active: true }">
|
||||
<div class="node-icon">📱</div>
|
||||
<div class="node-title">浏览器</div>
|
||||
<div class="node-desc" v-if="step === 0">要去 www.google.com</div>
|
||||
<div class="node-desc" v-if="step === 1">问 114查号台...</div>
|
||||
<div class="node-desc success" v-if="step === 2">收到: 142... 发车!</div>
|
||||
</div>
|
||||
|
||||
<div class="flow-path-wrapper">
|
||||
<div class="flow-path" :class="{ active: step >= 0 }">
|
||||
<span class="path-label">询问坐标</span>
|
||||
<div class="moving-dot" v-if="step === 1"></div>
|
||||
</div>
|
||||
<div class="flow-path reverse" :class="{ active: step === 2 }">
|
||||
<span class="path-label">返回 IP</span>
|
||||
<div class="moving-dot reverse" v-if="step === 2"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 查号台 -->
|
||||
<div class="flow-node dns-node" :class="{ active: step >= 1, flash: step === 1 }">
|
||||
<div class="node-icon">📞</div>
|
||||
<div class="node-title">114查号台 (DNS)</div>
|
||||
<div class="node-desc" v-if="step === 0">待命</div>
|
||||
<div class="node-desc" v-if="step === 1">正在翻地址簿...</div>
|
||||
<div class="node-desc success" v-if="step === 2">找到啦: 142.250.80.46</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="action-bar">
|
||||
<button class="action-btn" @click="runDemo" :disabled="isRunning">
|
||||
{{ isRunning ? '查询中...' : (step === 2 ? '重新查询' : '开始 DNS 查询') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="demo-status">{{ statusText }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// Simplified: No need for complex i18n logic anymore as we display both.
|
||||
defineProps({
|
||||
lang: String // Accepted but ignored
|
||||
})
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const step = ref(0)
|
||||
const isRunning = ref(false)
|
||||
const statusList = [
|
||||
'点击按钮,告诉浏览器你不知道 Google 服务器在哪',
|
||||
'浏览器向营运商查号台 (DNS) 请求数字坐标...',
|
||||
'拿到具体的 IP 地址,准备开始发车通信!'
|
||||
]
|
||||
|
||||
const statusText = computed(() => statusList[step.value])
|
||||
|
||||
const runDemo = () => {
|
||||
if (isRunning.value) return
|
||||
step.value = 0
|
||||
isRunning.value = true
|
||||
|
||||
setTimeout(() => {
|
||||
step.value = 1
|
||||
setTimeout(() => {
|
||||
step.value = 2
|
||||
isRunning.value = false
|
||||
}, 1500)
|
||||
}, 300)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dns-lookup-demo {
|
||||
.custom-demo-base {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
background: var(--vp-c-bg);
|
||||
padding: 1.5rem;
|
||||
margin: 0.5rem 0;
|
||||
font-family: var(--vp-font-family-mono);
|
||||
}
|
||||
|
||||
.concept-explanation {
|
||||
border-radius: 8px;
|
||||
background: var(--vp-c-bg-soft);
|
||||
padding: 0.75rem;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 2rem;
|
||||
padding: 1rem 1.2rem;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.why-text {
|
||||
margin: 0 0 0.5rem 0;
|
||||
color: var(--vp-c-brand);
|
||||
.demo-label {
|
||||
font-size: 0.78rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.why-desc-en {
|
||||
margin: 0 0 0.5rem 0;
|
||||
color: var(--vp-c-text-1);
|
||||
line-height: 1.5;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.why-desc-zh {
|
||||
margin: 0;
|
||||
color: var(--vp-c-text-2);
|
||||
line-height: 1.5;
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0.75rem;
|
||||
letter-spacing: 0.2px;
|
||||
}
|
||||
|
||||
.demo-stage {
|
||||
.demo-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
gap: 2rem;
|
||||
padding: 2rem;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
background: var(--vp-c-bg);
|
||||
}
|
||||
|
||||
.input-area,
|
||||
.output-area {
|
||||
.demo-status {
|
||||
margin-top: 0.75rem;
|
||||
font-size: 0.85rem;
|
||||
color: var(--vp-c-text-3);
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.lookup-flow {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-3);
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.fake-input,
|
||||
.fake-output {
|
||||
background: var(--vp-c-bg-alt);
|
||||
padding: 0.8rem;
|
||||
border-radius: 6px;
|
||||
font-size: 1.2rem;
|
||||
font-weight: bold;
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.fake-input {
|
||||
border-color: #3b82f6;
|
||||
color: #3b82f6;
|
||||
}
|
||||
|
||||
.fake-output {
|
||||
border-color: #10b981;
|
||||
color: #10b981;
|
||||
}
|
||||
|
||||
.process-animation {
|
||||
.flow-node {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
justify-content: center;
|
||||
width: 140px;
|
||||
height: 140px;
|
||||
border-radius: 50%;
|
||||
border: 4px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg-alt);
|
||||
transition: all 0.3s;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.dns-box {
|
||||
background: #fffbeb;
|
||||
border: 2px solid #f59e0b;
|
||||
padding: 1rem 2rem;
|
||||
border-radius: 12px;
|
||||
text-align: center;
|
||||
width: 240px; /* Slightly wider for bilingual text */
|
||||
.flow-node.active {
|
||||
border-color: var(--vp-c-brand-1, #3b82f6);
|
||||
background: var(--vp-c-brand-soft, #eff6ff);
|
||||
}
|
||||
|
||||
.html.dark .dns-box {
|
||||
background: #451a03;
|
||||
.flow-node.flash {
|
||||
box-shadow: 0 0 0 6px rgba(59, 130, 246, 0.2);
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-size: 2rem;
|
||||
.dns-node.active {
|
||||
border-color: var(--vp-c-success-1, #10b981);
|
||||
background: var(--vp-c-success-soft, #ecfdf5);
|
||||
}
|
||||
.dns-node.flash {
|
||||
box-shadow: 0 0 0 6px rgba(16, 185, 129, 0.2);
|
||||
}
|
||||
|
||||
.node-icon {
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-weight: bold;
|
||||
color: #d97706;
|
||||
.node-title {
|
||||
font-size: 0.9rem;
|
||||
font-weight: bold;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.desc {
|
||||
font-size: 0.8rem;
|
||||
color: #b45309;
|
||||
.node-desc {
|
||||
font-size: 0.75rem;
|
||||
color: var(--vp-c-text-2);
|
||||
text-align: center;
|
||||
margin-top: 0.2rem;
|
||||
padding: 0 0.5rem;
|
||||
min-height: 2.2em;
|
||||
}
|
||||
|
||||
.arrow-down {
|
||||
font-size: 1.5rem;
|
||||
color: var(--vp-c-text-3);
|
||||
animation: bounce 2s infinite;
|
||||
.node-desc.success {
|
||||
color: var(--vp-c-success-1, #10b981);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@keyframes bounce {
|
||||
0%,
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
.flow-path-wrapper {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
height: 60px;
|
||||
margin: 0 -20px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.flow-path {
|
||||
height: 2px;
|
||||
background: var(--vp-c-divider);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
}
|
||||
|
||||
.flow-path.active {
|
||||
background: var(--vp-c-brand-1, #3b82f6);
|
||||
}
|
||||
|
||||
.flow-path.reverse.active {
|
||||
background: var(--vp-c-success-1, #10b981);
|
||||
}
|
||||
|
||||
.path-label {
|
||||
position: absolute;
|
||||
top: -24px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-size: 0.75rem;
|
||||
color: var(--vp-c-text-2);
|
||||
background: var(--vp-c-bg);
|
||||
padding: 0 0.4rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.flow-path.reverse .path-label {
|
||||
top: auto;
|
||||
bottom: -24px;
|
||||
}
|
||||
|
||||
.moving-dot {
|
||||
position: absolute;
|
||||
top: -4px;
|
||||
left: 0;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
background: var(--vp-c-brand-1, #3b82f6);
|
||||
animation: moveRight 1.5s linear infinite;
|
||||
}
|
||||
|
||||
.moving-dot.reverse {
|
||||
background: var(--vp-c-success-1, #10b981);
|
||||
left: auto;
|
||||
right: 0;
|
||||
animation: moveLeft 1.5s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes moveRight {
|
||||
0% { left: 0%; opacity: 0; }
|
||||
10% { opacity: 1; }
|
||||
90% { opacity: 1; }
|
||||
100% { left: 100%; opacity: 0; }
|
||||
}
|
||||
|
||||
@keyframes moveLeft {
|
||||
0% { right: 0%; opacity: 0; }
|
||||
10% { opacity: 1; }
|
||||
90% { opacity: 1; }
|
||||
100% { right: 100%; opacity: 0; }
|
||||
}
|
||||
|
||||
.action-bar {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
background: var(--vp-c-brand-1, #3b82f6);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.6rem 1.5rem;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.action-btn:hover:not(:disabled) {
|
||||
background: var(--vp-c-brand-2, #2563eb);
|
||||
}
|
||||
|
||||
.action-btn:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.lookup-flow {
|
||||
flex-direction: column;
|
||||
gap: 2rem;
|
||||
}
|
||||
50% {
|
||||
transform: translateY(5px);
|
||||
.flow-path-wrapper {
|
||||
height: 40px;
|
||||
width: 2px;
|
||||
margin: -10px 0;
|
||||
}
|
||||
.flow-path {
|
||||
width: 2px;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
}
|
||||
.path-label {
|
||||
top: 50%;
|
||||
left: 10px;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
.flow-path.reverse .path-label {
|
||||
left: auto;
|
||||
right: 10px;
|
||||
}
|
||||
.moving-dot, .moving-dot.reverse {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user