feat: 更新附录交互组件和文档
This commit is contained in:
@@ -72,7 +72,7 @@
|
||||
@mouseenter="hoveredPart = 'card'"
|
||||
@mouseleave="hoveredPart = null"
|
||||
>
|
||||
<div class="card">
|
||||
<div class="player">
|
||||
</div>
|
||||
<div
|
||||
class="line indent-3"
|
||||
@@ -83,7 +83,7 @@
|
||||
@mouseenter="hoveredPart = 'img'"
|
||||
@mouseleave="hoveredPart = null"
|
||||
>
|
||||
<img class="icon" src="castle.png" />
|
||||
<img class="cover" src="cat.jpg" />
|
||||
</div>
|
||||
<div
|
||||
class="line indent-3"
|
||||
@@ -94,7 +94,7 @@
|
||||
@mouseenter="hoveredPart = 'title'"
|
||||
@mouseleave="hoveredPart = null"
|
||||
>
|
||||
<h2 class="title">乐高城堡</h2>
|
||||
<h2 class="title">搞笑猫咪合集</h2>
|
||||
</div>
|
||||
<div
|
||||
class="line indent-3"
|
||||
@@ -105,7 +105,7 @@
|
||||
@mouseenter="hoveredPart = 'btn'"
|
||||
@mouseleave="hoveredPart = null"
|
||||
>
|
||||
<button class="btn">购买</button>
|
||||
<button class="btn">▶️ 播放</button>
|
||||
</div>
|
||||
<div
|
||||
class="line indent-2"
|
||||
@@ -154,7 +154,7 @@
|
||||
@mouseenter="hoveredPart = 'card'"
|
||||
@mouseleave="hoveredPart = null"
|
||||
>
|
||||
.card { display: flex; padding: 10px; }
|
||||
.player { margin: auto; padding: 20px; }
|
||||
</div>
|
||||
<div
|
||||
class="line"
|
||||
@@ -165,7 +165,7 @@
|
||||
@mouseenter="hoveredPart = 'img'"
|
||||
@mouseleave="hoveredPart = null"
|
||||
>
|
||||
.icon { width: 50px; height: 50px; }
|
||||
.cover { width: 100%; height: 200px; }
|
||||
</div>
|
||||
<!-- Style properties -->
|
||||
<div
|
||||
@@ -177,7 +177,7 @@
|
||||
@mouseenter="hoveredPart = 'title'"
|
||||
@mouseleave="hoveredPart = null"
|
||||
>
|
||||
.title { color: red; }
|
||||
.title { color: #fb7299; /* B站主题色 */ }
|
||||
</div>
|
||||
<div
|
||||
class="line"
|
||||
@@ -188,7 +188,7 @@
|
||||
@mouseenter="hoveredPart = 'btn'"
|
||||
@mouseleave="hoveredPart = null"
|
||||
>
|
||||
.btn { background: blue; }
|
||||
.btn { background: #00aeec; color: white; }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -233,7 +233,7 @@
|
||||
@mouseenter.stop="hoveredPart = 'card'"
|
||||
@mouseleave="hoveredPart = null"
|
||||
>
|
||||
<span class="block-label">div.card</span>
|
||||
<span class="block-label">div.player</span>
|
||||
|
||||
<!-- Image -->
|
||||
<div
|
||||
@@ -245,11 +245,11 @@
|
||||
@mouseenter.stop="hoveredPart = 'img'"
|
||||
@mouseleave="hoveredPart = null"
|
||||
>
|
||||
<span class="block-label">img.icon</span>
|
||||
<span class="block-label">img.cover</span>
|
||||
<span
|
||||
v-if="currentStep >= 3"
|
||||
class="content-img"
|
||||
>🏰</span>
|
||||
>🐈</span>
|
||||
</div>
|
||||
|
||||
<!-- Title -->
|
||||
@@ -267,7 +267,7 @@
|
||||
<span
|
||||
v-if="currentStep >= 3"
|
||||
class="content"
|
||||
>乐高城堡</span>
|
||||
>搞笑猫咪合集</span>
|
||||
</div>
|
||||
|
||||
<!-- Button -->
|
||||
@@ -285,7 +285,7 @@
|
||||
<span
|
||||
v-if="currentStep >= 3"
|
||||
class="content-btn"
|
||||
>购买</span>
|
||||
>▶️ 播放</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -332,26 +332,26 @@ import { ref } from 'vue'
|
||||
const steps = [
|
||||
{
|
||||
label: 'DOM (搭骨架)',
|
||||
title: '1. 搭建骨架 (DOM)',
|
||||
desc: '浏览器工头 (Parser) 解析 HTML 代码,构建出完整的文档树结构。注意:即使代码中省略了 html/body,浏览器也会自动补全。',
|
||||
title: '1. 搭建骨架 (DOM 解析)',
|
||||
desc: '浏览器工厂看懂了 HTML 代码,搭建好了页面的“骨架”(比如哪里是包裹盒 div,哪里是按钮 button)。',
|
||||
resultTitle: 'DOM 树结构'
|
||||
},
|
||||
{
|
||||
label: 'Style (上色)',
|
||||
title: '2. 计算样式 (Recalculate Style)',
|
||||
desc: '装修工 (CSS Parser) 匹配 CSS 规则。比如发现 .title 需要红色,.btn 需要蓝色背景。此时只关心"长什么样",不关心"在哪"。',
|
||||
resultTitle: '附带样式的节点'
|
||||
label: 'Style (看图纸)',
|
||||
title: '2. 匹配样式 (CSS 解析)',
|
||||
desc: '仔细看了眼配色的说明书。比如发现 .title 字体要是粉色的,.btn 背景要是蓝色的(此时只在脑子里确立样式,但不计算尺寸)。',
|
||||
resultTitle: '获取了各种配置规则'
|
||||
},
|
||||
{
|
||||
label: 'Layout (排版)',
|
||||
title: '3. 布局排版 (Layout/Reflow)',
|
||||
desc: '测量员 (Layout) 根据 display:flex 和 padding 等属性,计算每个盒子的精确位置和大小。图片在左,文字在右。',
|
||||
resultTitle: '几何布局'
|
||||
label: 'Layout (定尺寸)',
|
||||
title: '3. 排版规划 (Layout)',
|
||||
desc: '拿尺子量每个骨架的大小。考虑到用户的屏幕尺寸,精确计算出猫咪的图片要多高、播放按钮要挤到哪个坐标上。',
|
||||
resultTitle: '排版布局盒子'
|
||||
},
|
||||
{
|
||||
label: 'Paint (绘制)',
|
||||
title: '4. 像素绘制 (Paint)',
|
||||
desc: '画家 (Paint) 按照计算好的位置和样式,真正把像素点画在屏幕上。最终你看到了一个完整的商品卡片。',
|
||||
title: '4. 像素上色 (Paint)',
|
||||
desc: '根据前面的几何位置和颜色计划,正式拿起画笔,将一个个像素填到你的屏幕上,一个可以看视频的播放器就诞生了。',
|
||||
resultTitle: '最终画面'
|
||||
}
|
||||
]
|
||||
@@ -576,34 +576,39 @@ const hoveredPart = ref(null)
|
||||
|
||||
/* Step 2: Style */
|
||||
.block-box.title.styled {
|
||||
color: red; /* Text color applied but not painted yet */
|
||||
border: 1px solid red; /* Visual cue for style applied */
|
||||
background: #fee2e2;
|
||||
color: #fb7299;
|
||||
border: 1px solid #fb7299;
|
||||
background: #fdf2f8;
|
||||
}
|
||||
|
||||
.block-box.btn.styled {
|
||||
background: blue;
|
||||
background: #00aeec;
|
||||
color: white;
|
||||
border: 1px solid blue;
|
||||
border: 1px solid #00aeec;
|
||||
}
|
||||
|
||||
/* Step 3: Layout */
|
||||
.block-box.card.layout {
|
||||
display: flex;
|
||||
flex-direction: row; /* Horizontal layout */
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 15px;
|
||||
background: white;
|
||||
border: 1px solid #ccc;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.block-box.img.layout {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
width: 100%;
|
||||
height: 120px;
|
||||
background: #eee;
|
||||
border: none;
|
||||
font-size: 3rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.block-box.title.layout {
|
||||
@@ -614,9 +619,11 @@ const hoveredPart = ref(null)
|
||||
}
|
||||
|
||||
.block-box.btn.layout {
|
||||
margin-left: auto; /* Push to right */
|
||||
padding: 5px 15px;
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Content visibility for Paint step */
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
<strong>为什么需要 DNS?(查导航)</strong>
|
||||
</p>
|
||||
<p class="why-desc-zh">
|
||||
你知道店铺名字叫 "Shop.com",但快递员需要知道具体的经纬度坐标 (IP 地址)
|
||||
你知道店铺名字叫 "bilibili.com",但快递员需要知道具体的经纬度坐标 (IP 地址)
|
||||
才能送达。
|
||||
<br>
|
||||
DNS 就像是<strong>地图导航</strong>,输入店名,它告诉你具体的坐标。
|
||||
DNS 就像是<strong>地图导航</strong>,输入店名,它通过“114查号台”帮你找到坐标。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<div class="input-area">
|
||||
<span class="label">店铺名称 (域名)</span>
|
||||
<div class="fake-input">
|
||||
shop.com
|
||||
bilibili.com
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -29,10 +29,10 @@
|
||||
🧭
|
||||
</div>
|
||||
<div class="title">
|
||||
DNS (地图导航)
|
||||
DNS (查号台)
|
||||
</div>
|
||||
<div class="desc">
|
||||
正在查找 shop.com 的位置...
|
||||
正在查询 bilibili.com 的 IP...
|
||||
</div>
|
||||
</div>
|
||||
<div class="arrow-down">
|
||||
@@ -41,9 +41,9 @@
|
||||
</div>
|
||||
|
||||
<div class="output-area">
|
||||
<span class="label">GPS 坐标 (IP 地址)</span>
|
||||
<span class="label">精准坐标 (IP 地址)</span>
|
||||
<div class="fake-output">
|
||||
93.184.216.34
|
||||
110.43.12.55
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -169,20 +169,20 @@ const props = defineProps({
|
||||
|
||||
const t = {
|
||||
send: '提交订单 (发送请求)',
|
||||
noRequests: '购物车是空的 (无请求)',
|
||||
placeholder: '点击 "提交订单" 向店员购买玩具',
|
||||
general: '订单详情 (General)',
|
||||
requestUrl: '商品地址 (URL)',
|
||||
noRequests: '还没发请求 (网络空闲)',
|
||||
placeholder: '点击 "提交订单" 向服务器索要页面',
|
||||
general: '请求概要 (General)',
|
||||
requestUrl: '目标地址 (URL)',
|
||||
requestMethod: '操作类型 (Method)',
|
||||
statusCode: '店员回复 (Status)',
|
||||
responseHeaders: '包裹标签 (Headers)',
|
||||
statusCode: '服务器回复状态 (Status)',
|
||||
responseHeaders: '包裹标签 / 补充说明 (Headers)',
|
||||
tabs: {
|
||||
headers: '订单信息',
|
||||
response: '包裹内容',
|
||||
preview: '玩具预览'
|
||||
headers: '头部信息(Headers)',
|
||||
response: '代码内容(Response)',
|
||||
preview: '大致预览(Preview)'
|
||||
},
|
||||
cols: {
|
||||
name: '商品',
|
||||
name: '请求体',
|
||||
status: '状态',
|
||||
type: '类型',
|
||||
time: '耗时'
|
||||
@@ -190,7 +190,7 @@ const t = {
|
||||
}
|
||||
|
||||
const method = ref('GET')
|
||||
const path = ref('/toys/lego-castle')
|
||||
const path = ref('/video/BV1xx411c7mD')
|
||||
const loading = ref(false)
|
||||
const requestSent = ref(false)
|
||||
const activeTab = ref('headers')
|
||||
@@ -210,20 +210,33 @@ const sendRequest = async () => {
|
||||
loading.value = false
|
||||
|
||||
if (method.value === 'GET') {
|
||||
responseStatus.value = '200 OK (有货)'
|
||||
responseStatus.value = '200 OK (交易成功)'
|
||||
responseHeaders.value = {
|
||||
'Content-Type': 'application/json (积木)',
|
||||
Date: new Date().toLocaleString(),
|
||||
Store: '乐高官方店'
|
||||
'Content-Type': 'text/html; charset=utf-8',
|
||||
'Server': 'BWS/1.1 (Bilibili Web Server)',
|
||||
'Date': new Date().toUTCString()
|
||||
}
|
||||
responseBody.value = `{\n "id": 101,\n "name": "Lego Castle",\n "pieces": 500,\n "price": "$99"\n}`
|
||||
responseBody.value = `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>【B站】超级搞笑的猫咪合集</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>超级搞笑的猫咪合集</h1>
|
||||
<div class="player">
|
||||
<img src="cat_cover.jpg" alt="封面" />
|
||||
<button>▶️ 播放</button>
|
||||
</div>
|
||||
</body>
|
||||
</html>`
|
||||
} else {
|
||||
responseStatus.value = '201 Created (下单成功)'
|
||||
responseStatus.value = '201 Created (操作成功)'
|
||||
responseHeaders.value = {
|
||||
'Content-Type': 'application/json',
|
||||
Date: new Date().toLocaleString()
|
||||
'Server': 'BWS/1.1',
|
||||
'Date': new Date().toUTCString()
|
||||
}
|
||||
responseBody.value = `{\n "success": true,\n "message": "Order placed"\n}`
|
||||
responseBody.value = `{\n "success": true,\n "message": "点赞成功!"\n}`
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -139,21 +139,21 @@ const props = defineProps({
|
||||
|
||||
// Bilingual text directly
|
||||
const t = {
|
||||
statusLabel: '通话状态',
|
||||
connect: '拨打电话',
|
||||
reset: '挂断重拨',
|
||||
client: '我 (顾客)',
|
||||
server: '玩具店',
|
||||
statusLabel: '连接状态',
|
||||
connect: '建立连接',
|
||||
reset: '断开重连',
|
||||
client: '我 (浏览器)',
|
||||
server: '对面 (B站服务器)',
|
||||
status: {
|
||||
closed: '未通话',
|
||||
handshaking: '正在拨号...',
|
||||
established: '通话中 (连接已建立)'
|
||||
closed: '未连接',
|
||||
handshaking: '正在打招呼确认通道...',
|
||||
established: 'TCP 通道已建立 (ESTABLISHED)'
|
||||
},
|
||||
steps: {
|
||||
0: '点击 "拨打电话" 开始确认店铺是否营业。',
|
||||
1: '步骤 1: 我问 "喂?有人在吗?" (SYN)',
|
||||
2: '步骤 2: 店员答 "在的!请问有什么事?" (SYN-ACK)',
|
||||
3: '步骤 3: 我说 "太好了,我想买东西!" (ACK)'
|
||||
0: '点击 "建立连接" 开始三次握手(电话试音)。',
|
||||
1: '第一次握手: "喂,服务器老哥在吗?我能发信息,你能收到吗?" (SYN)',
|
||||
2: '第二次握手: "喂喂在的!我收到了!那你现在能听到我说话吗?" (SYN-ACK)',
|
||||
3: '第三次握手: "妥了,我也能听到!通道没问题,准备看视频!" (ACK)'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -109,14 +109,14 @@ const props = defineProps({
|
||||
}
|
||||
})
|
||||
|
||||
const inputUrl = ref('https://shop.com/toys/lego-castle?color=red#summary')
|
||||
const inputUrl = ref('https://www.bilibili.com/video/BV1xx411c7mD?t=60#comments')
|
||||
const highlightedPart = ref(null)
|
||||
|
||||
const icons = {
|
||||
protocol: '🚛',
|
||||
host: '🏢',
|
||||
port: '🚪',
|
||||
pathname: '🧸',
|
||||
pathname: '📺',
|
||||
search: '📝',
|
||||
hash: '📍'
|
||||
}
|
||||
@@ -125,18 +125,18 @@ const labels = {
|
||||
protocol: '交通方式 (Protocol)',
|
||||
host: '店铺地址 (Host)',
|
||||
port: '大门号 (Port)',
|
||||
pathname: '商品位置 (Path)',
|
||||
search: '备注要求 (Query)',
|
||||
hash: '快速定位 (Hash)'
|
||||
pathname: '具体货架 (Path)',
|
||||
search: '特殊要求 (Search/Query)',
|
||||
hash: '直接跳转 (Hash)'
|
||||
}
|
||||
|
||||
const descriptions = {
|
||||
protocol: '怎么去?(例如 https = 坐装甲车去,很安全)',
|
||||
host: '去哪家店?(域名,例如 shop.com)',
|
||||
port: '从哪个门进?(默认 443 号门)',
|
||||
pathname: '商品在哪个货架?(路径)',
|
||||
search: '给店员的备注 (例如 ?color=red)',
|
||||
hash: '直接翻到说明书第几页 (锚点)'
|
||||
protocol: '怎么去?(https = 坐押运车去,比 http 安全)',
|
||||
host: '去哪家店?(域名:例如 www.bilibili.com)',
|
||||
port: '走哪个门?(默认隐藏了 443 端口号)',
|
||||
pathname: '拿什么货?(去 /video 区拿编号为 BV... 的视频)',
|
||||
search: '给店员的备注说明 (例如 ?t=60 表示要求从 60 秒开始看)',
|
||||
hash: '拿到货后自己做的事 (例如滚动到评论区 #comments)'
|
||||
}
|
||||
|
||||
const parsedUrl = computed(() => {
|
||||
|
||||
Reference in New Issue
Block a user