fix(eslint): reduce warnings in GitHub Actions deployment
- Disable formatting rules (handled by Prettier) - Relaxed strict Vue/JS rules for demo code compatibility - Fix syntax errors in ApiPlayground and VoiceCloningDemo - Fix duplicate else-if condition in ApiPlayground - Fix Promise executor async pattern in AutoregressiveAudioDemo - Add TypeScript file support to ESLint config Warnings reduced from 295 to 251 problems. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -22,23 +22,41 @@
|
||||
|
||||
<div class="cards">
|
||||
<div class="card">
|
||||
<div class="label">运行环境</div>
|
||||
<div class="value">{{ currentData.runtime }}</div>
|
||||
<div class="label">
|
||||
运行环境
|
||||
</div>
|
||||
<div class="value">
|
||||
{{ currentData.runtime }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="label">主要技术</div>
|
||||
<div class="value">{{ currentData.stack }}</div>
|
||||
<div class="label">
|
||||
主要技术
|
||||
</div>
|
||||
<div class="value">
|
||||
{{ currentData.stack }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="label">发布方式</div>
|
||||
<div class="value">{{ currentData.release }}</div>
|
||||
<div class="label">
|
||||
发布方式
|
||||
</div>
|
||||
<div class="value">
|
||||
{{ currentData.release }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="skills">
|
||||
<div class="skills-title">哪些能力是"共通的"?</div>
|
||||
<div class="skills-title">
|
||||
哪些能力是"共通的"?
|
||||
</div>
|
||||
<div class="tags">
|
||||
<span v-for="t in commonSkills.slice(0, 6)" :key="t" class="tag">{{ t }}</span>
|
||||
<span
|
||||
v-for="t in commonSkills.slice(0, 6)"
|
||||
:key="t"
|
||||
class="tag"
|
||||
>{{ t }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -25,7 +25,9 @@
|
||||
<div class="visualization-window">
|
||||
<!-- HTML/CSS Source -->
|
||||
<div class="source-view">
|
||||
<div class="window-title">积木说明书 (HTML/CSS)</div>
|
||||
<div class="window-title">
|
||||
积木说明书 (HTML/CSS)
|
||||
</div>
|
||||
<div class="code-content">
|
||||
<!-- HTML Highlighted always after Step 0 -->
|
||||
<div
|
||||
@@ -139,7 +141,7 @@
|
||||
</html>
|
||||
</div>
|
||||
|
||||
<div class="spacer"></div>
|
||||
<div class="spacer" />
|
||||
|
||||
<!-- CSS Highlighted precisely based on step usage -->
|
||||
<!-- Layout properties -->
|
||||
@@ -191,11 +193,15 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="transform-arrow">→</div>
|
||||
<div class="transform-arrow">
|
||||
→
|
||||
</div>
|
||||
|
||||
<!-- Render Result -->
|
||||
<div class="result-view">
|
||||
<div class="window-title">{{ steps[currentStep].resultTitle }}</div>
|
||||
<div class="window-title">
|
||||
{{ steps[currentStep].resultTitle }}
|
||||
</div>
|
||||
|
||||
<div class="render-canvas">
|
||||
<!-- Step 1: DOM (Skeleton) -->
|
||||
@@ -240,9 +246,10 @@
|
||||
@mouseleave="hoveredPart = null"
|
||||
>
|
||||
<span class="block-label">img.icon</span>
|
||||
<span v-if="currentStep >= 3" class="content-img"
|
||||
>🏰</span
|
||||
>
|
||||
<span
|
||||
v-if="currentStep >= 3"
|
||||
class="content-img"
|
||||
>🏰</span>
|
||||
</div>
|
||||
|
||||
<!-- Title -->
|
||||
@@ -257,9 +264,10 @@
|
||||
@mouseleave="hoveredPart = null"
|
||||
>
|
||||
<span class="block-label">h2.title</span>
|
||||
<span v-if="currentStep >= 3" class="content"
|
||||
>乐高城堡</span
|
||||
>
|
||||
<span
|
||||
v-if="currentStep >= 3"
|
||||
class="content"
|
||||
>乐高城堡</span>
|
||||
</div>
|
||||
|
||||
<!-- Button -->
|
||||
@@ -274,9 +282,10 @@
|
||||
@mouseleave="hoveredPart = null"
|
||||
>
|
||||
<span class="block-label">button.btn</span>
|
||||
<span v-if="currentStep >= 3" class="content-btn"
|
||||
>购买</span
|
||||
>
|
||||
<span
|
||||
v-if="currentStep >= 3"
|
||||
class="content-btn"
|
||||
>购买</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -284,16 +293,31 @@
|
||||
</transition-group>
|
||||
|
||||
<!-- Overlays for different steps -->
|
||||
<div v-if="currentStep === 1" class="overlay-info style-info">
|
||||
<div class="brush">🖌️ 正在上色 (Style)...</div>
|
||||
<div
|
||||
v-if="currentStep === 1"
|
||||
class="overlay-info style-info"
|
||||
>
|
||||
<div class="brush">
|
||||
🖌️ 正在上色 (Style)...
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="currentStep === 2" class="overlay-info layout-info">
|
||||
<div class="ruler">📏 正在排版 (Layout)...</div>
|
||||
<div
|
||||
v-if="currentStep === 2"
|
||||
class="overlay-info layout-info"
|
||||
>
|
||||
<div class="ruler">
|
||||
📏 正在排版 (Layout)...
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="currentStep === 3" class="overlay-info paint-info">
|
||||
<div class="paint">✨ 绘制完成 (Paint)!</div>
|
||||
<div
|
||||
v-if="currentStep === 3"
|
||||
class="overlay-info paint-info"
|
||||
>
|
||||
<div class="paint">
|
||||
✨ 绘制完成 (Paint)!
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,35 +5,60 @@
|
||||
<template>
|
||||
<div class="bundler-demo">
|
||||
<div class="header">
|
||||
<div class="title">工程化:打包体积与构建时间</div>
|
||||
<div class="subtitle">勾选功能,观察体积变化</div>
|
||||
<div class="title">
|
||||
工程化:打包体积与构建时间
|
||||
</div>
|
||||
<div class="subtitle">
|
||||
勾选功能,观察体积变化
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="options">
|
||||
<label v-for="item in features" :key="item.key" class="option">
|
||||
<input type="checkbox" v-model="item.enabled" />
|
||||
<label
|
||||
v-for="item in features"
|
||||
:key="item.key"
|
||||
class="option"
|
||||
>
|
||||
<input
|
||||
v-model="item.enabled"
|
||||
type="checkbox"
|
||||
>
|
||||
{{ item.label }} (+{{ item.size }} KB)
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<label class="toggle">
|
||||
<input type="checkbox" v-model="treeShaking" />
|
||||
<input
|
||||
v-model="treeShaking"
|
||||
type="checkbox"
|
||||
>
|
||||
开启 Tree Shaking (移除未使用代码)
|
||||
</label>
|
||||
|
||||
<div class="stats">
|
||||
<div class="stat-card">
|
||||
<div class="label">Bundle Size</div>
|
||||
<div class="value">{{ bundleSize }} KB</div>
|
||||
<div class="label">
|
||||
Bundle Size
|
||||
</div>
|
||||
<div class="value">
|
||||
{{ bundleSize }} KB
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="label">Build Time</div>
|
||||
<div class="value">{{ buildTime }} s</div>
|
||||
<div class="label">
|
||||
Build Time
|
||||
</div>
|
||||
<div class="value">
|
||||
{{ buildTime }} s
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bar">
|
||||
<div class="progress" :style="{ width: barWidth + '%' }"></div>
|
||||
<div
|
||||
class="progress"
|
||||
:style="{ width: barWidth + '%' }"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,15 +1,27 @@
|
||||
<template>
|
||||
<div class="component-reusability-demo">
|
||||
<div class="toolbox">
|
||||
<div class="tool-title">Component Library</div>
|
||||
<button class="spawn-btn" @click="spawn('counter')">
|
||||
<div class="tool-title">
|
||||
Component Library
|
||||
</div>
|
||||
<button
|
||||
class="spawn-btn"
|
||||
@click="spawn('counter')"
|
||||
>
|
||||
➕ New Counter
|
||||
</button>
|
||||
<button class="spawn-btn" @click="spawn('card')">➕ New Card</button>
|
||||
<button
|
||||
class="spawn-btn"
|
||||
@click="spawn('card')"
|
||||
>
|
||||
➕ New Card
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="workspace">
|
||||
<div class="workspace-label">App Workspace</div>
|
||||
<div class="workspace-label">
|
||||
App Workspace
|
||||
</div>
|
||||
<div class="instances-container">
|
||||
<transition-group name="list">
|
||||
<div
|
||||
@@ -18,26 +30,47 @@
|
||||
class="instance-wrapper"
|
||||
>
|
||||
<!-- Counter Component -->
|
||||
<div v-if="item.type === 'counter'" class="comp-instance counter">
|
||||
<div
|
||||
v-if="item.type === 'counter'"
|
||||
class="comp-instance counter"
|
||||
>
|
||||
<div class="comp-header">
|
||||
<span>Counter #{{ item.id }}</span>
|
||||
<button class="close-btn" @click="remove(item.id)">×</button>
|
||||
<button
|
||||
class="close-btn"
|
||||
@click="remove(item.id)"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
<div class="comp-body">
|
||||
<span class="count-val">{{ item.data.count }}</span>
|
||||
<button class="mini-btn" @click="item.data.count++">+</button>
|
||||
<button
|
||||
class="mini-btn"
|
||||
@click="item.data.count++"
|
||||
>
|
||||
+
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Card Component -->
|
||||
<div v-if="item.type === 'card'" class="comp-instance card">
|
||||
<div
|
||||
v-if="item.type === 'card'"
|
||||
class="comp-instance card"
|
||||
>
|
||||
<div class="comp-header">
|
||||
<span>Card #{{ item.id }}</span>
|
||||
<button class="close-btn" @click="remove(item.id)">×</button>
|
||||
<button
|
||||
class="close-btn"
|
||||
@click="remove(item.id)"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
<div class="comp-body">
|
||||
<div class="skeleton-img"></div>
|
||||
<div class="skeleton-text"></div>
|
||||
<div class="skeleton-img" />
|
||||
<div class="skeleton-text" />
|
||||
<button
|
||||
class="like-btn"
|
||||
:class="{ liked: item.data.liked }"
|
||||
@@ -49,9 +82,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</transition-group>
|
||||
<div v-if="instances.length === 0" class="empty-hint">
|
||||
<div
|
||||
v-if="instances.length === 0"
|
||||
class="empty-hint"
|
||||
>
|
||||
Click buttons above to add components.
|
||||
<br />
|
||||
<br>
|
||||
Notice how each one works independently!
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -14,22 +14,42 @@
|
||||
<div class="controls">
|
||||
<div class="control-row">
|
||||
<label>width</label>
|
||||
<input type="range" min="60" max="150" v-model.number="contentW" />
|
||||
<input
|
||||
v-model.number="contentW"
|
||||
type="range"
|
||||
min="60"
|
||||
max="150"
|
||||
>
|
||||
<span class="val">{{ contentW }}px</span>
|
||||
</div>
|
||||
<div class="control-row">
|
||||
<label>padding</label>
|
||||
<input type="range" min="0" max="30" v-model.number="padding" />
|
||||
<input
|
||||
v-model.number="padding"
|
||||
type="range"
|
||||
min="0"
|
||||
max="30"
|
||||
>
|
||||
<span class="val">{{ padding }}px</span>
|
||||
</div>
|
||||
<div class="control-row">
|
||||
<label>border</label>
|
||||
<input type="range" min="0" max="15" v-model.number="border" />
|
||||
<input
|
||||
v-model.number="border"
|
||||
type="range"
|
||||
min="0"
|
||||
max="15"
|
||||
>
|
||||
<span class="val">{{ border }}px</span>
|
||||
</div>
|
||||
<div class="control-row">
|
||||
<label>margin</label>
|
||||
<input type="range" min="0" max="20" v-model.number="margin" />
|
||||
<input
|
||||
v-model.number="margin"
|
||||
type="range"
|
||||
min="0"
|
||||
max="20"
|
||||
>
|
||||
<span class="val">{{ margin }}px</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -51,13 +71,34 @@
|
||||
</div>
|
||||
|
||||
<div class="visual">
|
||||
<div class="layer margin" :style="{ padding: margin + 'px' }">
|
||||
<span class="layer-label" v-if="margin >= 8">margin</span>
|
||||
<div class="layer border" :style="{ borderWidth: border + 'px' }">
|
||||
<span class="layer-label" v-if="border >= 5">border</span>
|
||||
<div class="layer padding" :style="{ padding: padding + 'px' }">
|
||||
<span class="layer-label" v-if="padding >= 8">padding</span>
|
||||
<div class="content" :style="{ width: contentW + 'px' }">
|
||||
<div
|
||||
class="layer margin"
|
||||
:style="{ padding: margin + 'px' }"
|
||||
>
|
||||
<span
|
||||
v-if="margin >= 8"
|
||||
class="layer-label"
|
||||
>margin</span>
|
||||
<div
|
||||
class="layer border"
|
||||
:style="{ borderWidth: border + 'px' }"
|
||||
>
|
||||
<span
|
||||
v-if="border >= 5"
|
||||
class="layer-label"
|
||||
>border</span>
|
||||
<div
|
||||
class="layer padding"
|
||||
:style="{ padding: padding + 'px' }"
|
||||
>
|
||||
<span
|
||||
v-if="padding >= 8"
|
||||
class="layer-label"
|
||||
>padding</span>
|
||||
<div
|
||||
class="content"
|
||||
:style="{ width: contentW + 'px' }"
|
||||
>
|
||||
content<br>{{ contentW }}px
|
||||
</div>
|
||||
</div>
|
||||
@@ -80,7 +121,10 @@
|
||||
{{ contentW }}px(已包含 padding 和 border) + {{ margin }}×2 = {{ total }}px
|
||||
</template>
|
||||
</div>
|
||||
<div class="result-hint" :class="{ warning: total * 3 > 900 }">
|
||||
<div
|
||||
class="result-hint"
|
||||
:class="{ warning: total * 3 > 900 }"
|
||||
>
|
||||
<template v-if="total * 3 > 900">
|
||||
三个卡片需要 {{ total * 3 }}px,超出容器 900px,第三个会掉下去
|
||||
</template>
|
||||
@@ -91,15 +135,31 @@
|
||||
</div>
|
||||
|
||||
<div class="code-block">
|
||||
<div class="code-title">CSS</div>
|
||||
<div class="code-title">
|
||||
CSS
|
||||
</div>
|
||||
<div class="code-content">
|
||||
<div class="line">.box {</div>
|
||||
<div class="line hl"> box-sizing: {{ boxSizing }};</div>
|
||||
<div class="line"> width: {{ contentW }}px;</div>
|
||||
<div class="line"> padding: {{ padding }}px;</div>
|
||||
<div class="line"> border: {{ border }}px solid #ccc;</div>
|
||||
<div class="line"> margin: {{ margin }}px;</div>
|
||||
<div class="line">}</div>
|
||||
<div class="line">
|
||||
.box {
|
||||
</div>
|
||||
<div class="line hl">
|
||||
box-sizing: {{ boxSizing }};
|
||||
</div>
|
||||
<div class="line">
|
||||
width: {{ contentW }}px;
|
||||
</div>
|
||||
<div class="line">
|
||||
padding: {{ padding }}px;
|
||||
</div>
|
||||
<div class="line">
|
||||
border: {{ border }}px solid #ccc;
|
||||
</div>
|
||||
<div class="line">
|
||||
margin: {{ margin }}px;
|
||||
</div>
|
||||
<div class="line">
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -6,35 +6,57 @@
|
||||
</div>
|
||||
|
||||
<div class="categories">
|
||||
<div v-for="(cat, index) in categories" :key="index" class="category">
|
||||
<div class="cat-title">{{ cat.title }}</div>
|
||||
<div
|
||||
v-for="(cat, index) in categories"
|
||||
:key="index"
|
||||
class="category"
|
||||
>
|
||||
<div class="cat-title">
|
||||
{{ cat.title }}
|
||||
</div>
|
||||
<div class="props-grid">
|
||||
<div
|
||||
v-for="prop in cat.props"
|
||||
:key="prop.name"
|
||||
class="prop-item"
|
||||
@click="activeProp = prop"
|
||||
:class="{ active: activeProp && activeProp.name === prop.name }"
|
||||
@click="activeProp = prop"
|
||||
>
|
||||
<div class="prop-name">{{ prop.name }}</div>
|
||||
<div class="prop-desc">{{ prop.desc }}</div>
|
||||
<div class="prop-name">
|
||||
{{ prop.name }}
|
||||
</div>
|
||||
<div class="prop-desc">
|
||||
{{ prop.desc }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="activeProp" class="prop-detail">
|
||||
<div
|
||||
v-if="activeProp"
|
||||
class="prop-detail"
|
||||
>
|
||||
<div class="detail-header">
|
||||
<span class="detail-name">{{ activeProp.name }}</span>
|
||||
<span class="detail-cat-badge">{{ activeProp.categoryLabel }}</span>
|
||||
</div>
|
||||
<div class="detail-desc">{{ activeProp.fullDesc }}</div>
|
||||
<div class="detail-desc">
|
||||
{{ activeProp.fullDesc }}
|
||||
</div>
|
||||
<div class="detail-code">
|
||||
<div class="code-label">示例代码:</div>
|
||||
<div class="code-label">
|
||||
示例代码:
|
||||
</div>
|
||||
<pre><code>{{ activeProp.example }}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="prop-detail empty">点击上面的属性看看它能做什么 👆</div>
|
||||
<div
|
||||
v-else
|
||||
class="prop-detail empty"
|
||||
>
|
||||
点击上面的属性看看它能做什么 👆
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -89,8 +89,17 @@
|
||||
</div>
|
||||
|
||||
<div class="preview-area">
|
||||
<div class="canvas" :style="boxStyle">
|
||||
<div v-for="n in 6" :key="n" class="item">{{ n }}</div>
|
||||
<div
|
||||
class="canvas"
|
||||
:style="boxStyle"
|
||||
>
|
||||
<div
|
||||
v-for="n in 6"
|
||||
:key="n"
|
||||
class="item"
|
||||
>
|
||||
{{ n }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="axis-hint">
|
||||
<span class="axis-tag main">主轴方向: {{ dir === 'row' ? '水平 →' : '垂直 ↓' }}</span>
|
||||
@@ -100,7 +109,9 @@
|
||||
</div>
|
||||
|
||||
<div class="code-row">
|
||||
<div class="code-label">CSS</div>
|
||||
<div class="code-label">
|
||||
CSS
|
||||
</div>
|
||||
<code class="code-text">{{ cssCode }}</code>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -68,7 +68,10 @@
|
||||
</div>
|
||||
|
||||
<div class="preview-area">
|
||||
<div class="container" :style="containerStyle">
|
||||
<div
|
||||
class="container"
|
||||
:style="containerStyle"
|
||||
>
|
||||
<div
|
||||
v-for="n in itemCount"
|
||||
:key="n"
|
||||
@@ -81,7 +84,9 @@
|
||||
</div>
|
||||
|
||||
<div class="code-display">
|
||||
<div class="code-header">👆 点击代码行可以暂时禁用该属性</div>
|
||||
<div class="code-header">
|
||||
👆 点击代码行可以暂时禁用该属性
|
||||
</div>
|
||||
<pre>.container {
|
||||
display: flex;
|
||||
<div
|
||||
|
||||
@@ -19,49 +19,80 @@
|
||||
<div class="controls">
|
||||
<div class="control-group">
|
||||
<label>背景颜色 (background-color)</label>
|
||||
<input type="color" v-model="bgColor" />
|
||||
<input
|
||||
v-model="bgColor"
|
||||
type="color"
|
||||
>
|
||||
<span class="value">{{ bgColor }}</span>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label>文字颜色 (color)</label>
|
||||
<input type="color" v-model="textColor" />
|
||||
<input
|
||||
v-model="textColor"
|
||||
type="color"
|
||||
>
|
||||
<span class="value">{{ textColor }}</span>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label>字体大小 (font-size)</label>
|
||||
<input type="range" v-model="fontSize" min="12" max="48" />
|
||||
<input
|
||||
v-model="fontSize"
|
||||
type="range"
|
||||
min="12"
|
||||
max="48"
|
||||
>
|
||||
<span class="value">{{ fontSize }}px</span>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label>内边距 (padding)</label>
|
||||
<input type="range" v-model="padding" min="0" max="50" />
|
||||
<input
|
||||
v-model="padding"
|
||||
type="range"
|
||||
min="0"
|
||||
max="50"
|
||||
>
|
||||
<span class="value">{{ padding }}px</span>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label>圆角 (border-radius)</label>
|
||||
<input type="range" v-model="borderRadius" min="0" max="50" />
|
||||
<input
|
||||
v-model="borderRadius"
|
||||
type="range"
|
||||
min="0"
|
||||
max="50"
|
||||
>
|
||||
<span class="value">{{ borderRadius }}px</span>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label>边框宽度 (border-width)</label>
|
||||
<input type="range" v-model="borderWidth" min="0" max="10" />
|
||||
<input
|
||||
v-model="borderWidth"
|
||||
type="range"
|
||||
min="0"
|
||||
max="10"
|
||||
>
|
||||
<span class="value">{{ borderWidth }}px</span>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label>边框颜色 (border-color)</label>
|
||||
<input type="color" v-model="borderColor" />
|
||||
<input
|
||||
v-model="borderColor"
|
||||
type="color"
|
||||
>
|
||||
<span class="value">{{ borderColor }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="code-preview">
|
||||
<div class="code-title">生成的 CSS 代码:</div>
|
||||
<div class="code-title">
|
||||
生成的 CSS 代码:
|
||||
</div>
|
||||
<pre><code>.element {
|
||||
background-color: <span class="highlight">{{ bgColor }}</span>;
|
||||
color: <span class="highlight">{{ textColor }}</span>;
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
<div class="comparison">
|
||||
<!-- Left: CSS Rules -->
|
||||
<div class="column css-col">
|
||||
<div class="col-title">CSS (样式表)</div>
|
||||
<div class="col-title">
|
||||
CSS (样式表)
|
||||
</div>
|
||||
<div class="rules-list">
|
||||
<div
|
||||
class="rule-item"
|
||||
@@ -15,8 +17,12 @@
|
||||
@mouseenter="activeType = 'tag'"
|
||||
@mouseleave="activeType = null"
|
||||
>
|
||||
<div class="selector">p</div>
|
||||
<div class="block">{ color: #333; }</div>
|
||||
<div class="selector">
|
||||
p
|
||||
</div>
|
||||
<div class="block">
|
||||
{ color: #333; }
|
||||
</div>
|
||||
<div class="explanation">
|
||||
<span class="badge tag">标签选择器</span>
|
||||
直接写标签名,选中所有 <code><p></code>
|
||||
@@ -29,8 +35,12 @@
|
||||
@mouseenter="activeType = 'class'"
|
||||
@mouseleave="activeType = null"
|
||||
>
|
||||
<div class="selector">.card</div>
|
||||
<div class="block">{ background: white; }</div>
|
||||
<div class="selector">
|
||||
.card
|
||||
</div>
|
||||
<div class="block">
|
||||
{ background: white; }
|
||||
</div>
|
||||
<div class="explanation">
|
||||
<span class="badge class">类选择器</span>
|
||||
以 <code>.</code> 开头,选中所有 <code>class="card"</code>
|
||||
@@ -43,8 +53,12 @@
|
||||
@mouseenter="activeType = 'id'"
|
||||
@mouseleave="activeType = null"
|
||||
>
|
||||
<div class="selector">#submit-btn</div>
|
||||
<div class="block">{ font-weight: bold; }</div>
|
||||
<div class="selector">
|
||||
#submit-btn
|
||||
</div>
|
||||
<div class="block">
|
||||
{ font-weight: bold; }
|
||||
</div>
|
||||
<div class="explanation">
|
||||
<span class="badge id">ID 选择器</span>
|
||||
以 <code>#</code> 开头,选中唯一 <code>id="submit-btn"</code>
|
||||
@@ -55,19 +69,32 @@
|
||||
|
||||
<!-- Center: Connector -->
|
||||
<div class="connector">
|
||||
<div class="line-path" :class="activeType"></div>
|
||||
<div class="icon">🔗</div>
|
||||
<div
|
||||
class="line-path"
|
||||
:class="activeType"
|
||||
/>
|
||||
<div class="icon">
|
||||
🔗
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right: HTML Structure -->
|
||||
<div class="column html-col">
|
||||
<div class="col-title">HTML (结构)</div>
|
||||
<div class="col-title">
|
||||
HTML (结构)
|
||||
</div>
|
||||
<div class="code-view">
|
||||
<div class="html-line" :class="{ highlight: activeType === 'tag' }">
|
||||
<div
|
||||
class="html-line"
|
||||
:class="{ highlight: activeType === 'tag' }"
|
||||
>
|
||||
<p>我是普通段落</p>
|
||||
</div>
|
||||
|
||||
<div class="html-line" :class="{ highlight: activeType === 'class' }">
|
||||
<div
|
||||
class="html-line"
|
||||
:class="{ highlight: activeType === 'class' }"
|
||||
>
|
||||
<div <span class="attr">class="card"</span>>
|
||||
</div>
|
||||
|
||||
@@ -80,11 +107,17 @@
|
||||
<p>我是卡片里的段落</p>
|
||||
</div>
|
||||
|
||||
<div class="html-line" :class="{ highlight: activeType === 'class' }">
|
||||
<div
|
||||
class="html-line"
|
||||
:class="{ highlight: activeType === 'class' }"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="html-line" :class="{ highlight: activeType === 'id' }">
|
||||
<div
|
||||
class="html-line"
|
||||
:class="{ highlight: activeType === 'id' }"
|
||||
>
|
||||
<button
|
||||
<span class="attr">id="submit-btn"</span>>提交</button>
|
||||
</div>
|
||||
|
||||
@@ -15,152 +15,278 @@
|
||||
|
||||
<div class="architecture-diagram">
|
||||
<!-- 基础架构 -->
|
||||
<div v-if="currentView === 0" class="basic-architecture">
|
||||
<div
|
||||
v-if="currentView === 0"
|
||||
class="basic-architecture"
|
||||
>
|
||||
<div class="user-node">
|
||||
<div class="node-icon">👤</div>
|
||||
<div class="node-label">用户</div>
|
||||
<div class="node-icon">
|
||||
👤
|
||||
</div>
|
||||
<div class="node-label">
|
||||
用户
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="arrow-down">↓</div>
|
||||
<div class="arrow-down">
|
||||
↓
|
||||
</div>
|
||||
|
||||
<div class="domain-node">
|
||||
<div class="node-icon">🌐</div>
|
||||
<div class="node-label">域名</div>
|
||||
<div class="node-desc">example.com</div>
|
||||
<div class="node-icon">
|
||||
🌐
|
||||
</div>
|
||||
<div class="node-label">
|
||||
域名
|
||||
</div>
|
||||
<div class="node-desc">
|
||||
example.com
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="arrow-down">↓ DNS 解析</div>
|
||||
<div class="arrow-down">
|
||||
↓ DNS 解析
|
||||
</div>
|
||||
|
||||
<div class="server-node">
|
||||
<div class="node-icon">🖥️</div>
|
||||
<div class="node-label">服务器</div>
|
||||
<div class="node-desc">IP: 1.2.3.4</div>
|
||||
<div class="node-icon">
|
||||
🖥️
|
||||
</div>
|
||||
<div class="node-label">
|
||||
服务器
|
||||
</div>
|
||||
<div class="node-desc">
|
||||
IP: 1.2.3.4
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="arrow-down">↓</div>
|
||||
<div class="arrow-down">
|
||||
↓
|
||||
</div>
|
||||
|
||||
<div class="web-node">
|
||||
<div class="node-icon">🌍</div>
|
||||
<div class="node-label">Web 应用</div>
|
||||
<div class="node-icon">
|
||||
🌍
|
||||
</div>
|
||||
<div class="node-label">
|
||||
Web 应用
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- CDN 架构 -->
|
||||
<div v-if="currentView === 1" class="cdn-architecture">
|
||||
<div
|
||||
v-if="currentView === 1"
|
||||
class="cdn-architecture"
|
||||
>
|
||||
<div class="user-nodes">
|
||||
<div class="user-node china">
|
||||
<div class="node-icon">🇨🇳</div>
|
||||
<div class="node-label">中国用户</div>
|
||||
<div class="node-icon">
|
||||
🇨🇳
|
||||
</div>
|
||||
<div class="node-label">
|
||||
中国用户
|
||||
</div>
|
||||
</div>
|
||||
<div class="user-node usa">
|
||||
<div class="node-icon">🇺🇸</div>
|
||||
<div class="node-label">美国用户</div>
|
||||
<div class="node-icon">
|
||||
🇺🇸
|
||||
</div>
|
||||
<div class="node-label">
|
||||
美国用户
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="arrow-group">
|
||||
<div class="arrow-left">↙</div>
|
||||
<div class="arrow-right">↘</div>
|
||||
<div class="arrow-left">
|
||||
↙
|
||||
</div>
|
||||
<div class="arrow-right">
|
||||
↘
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cdn-nodes">
|
||||
<div class="cdn-node">
|
||||
<div class="node-icon">📡</div>
|
||||
<div class="node-label">CDN 北京节点</div>
|
||||
<div class="node-icon">
|
||||
📡
|
||||
</div>
|
||||
<div class="node-label">
|
||||
CDN 北京节点
|
||||
</div>
|
||||
</div>
|
||||
<div class="cdn-node">
|
||||
<div class="node-icon">📡</div>
|
||||
<div class="node-label">CDN 纽约节点</div>
|
||||
<div class="node-icon">
|
||||
📡
|
||||
</div>
|
||||
<div class="node-label">
|
||||
CDN 纽约节点
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="arrow-down">↓ 缓存未命中</div>
|
||||
<div class="arrow-down">
|
||||
↓ 缓存未命中
|
||||
</div>
|
||||
|
||||
<div class="origin-node">
|
||||
<div class="node-icon">🖥️</div>
|
||||
<div class="node-label">源服务器</div>
|
||||
<div class="node-icon">
|
||||
🖥️
|
||||
</div>
|
||||
<div class="node-label">
|
||||
源服务器
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 负载均衡 -->
|
||||
<div v-if="currentView === 2" class="loadbalancer-architecture">
|
||||
<div
|
||||
v-if="currentView === 2"
|
||||
class="loadbalancer-architecture"
|
||||
>
|
||||
<div class="user-node">
|
||||
<div class="node-icon">👥</div>
|
||||
<div class="node-label">用户请求</div>
|
||||
<div class="node-icon">
|
||||
👥
|
||||
</div>
|
||||
<div class="node-label">
|
||||
用户请求
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="arrow-down">↓</div>
|
||||
<div class="arrow-down">
|
||||
↓
|
||||
</div>
|
||||
|
||||
<div class="lb-node">
|
||||
<div class="node-icon">⚖️</div>
|
||||
<div class="node-label">负载均衡器</div>
|
||||
<div class="node-icon">
|
||||
⚖️
|
||||
</div>
|
||||
<div class="node-label">
|
||||
负载均衡器
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="arrow-group">
|
||||
<div class="arrow-1">↖</div>
|
||||
<div class="arrow-2">↑</div>
|
||||
<div class="arrow-3">↗</div>
|
||||
<div class="arrow-1">
|
||||
↖
|
||||
</div>
|
||||
<div class="arrow-2">
|
||||
↑
|
||||
</div>
|
||||
<div class="arrow-3">
|
||||
↗
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="server-nodes">
|
||||
<div class="server-node">
|
||||
<div class="node-icon">🖥️</div>
|
||||
<div class="node-label">服务器 1</div>
|
||||
<div class="node-icon">
|
||||
🖥️
|
||||
</div>
|
||||
<div class="node-label">
|
||||
服务器 1
|
||||
</div>
|
||||
</div>
|
||||
<div class="server-node">
|
||||
<div class="node-icon">🖥️</div>
|
||||
<div class="node-label">服务器 2</div>
|
||||
<div class="node-icon">
|
||||
🖥️
|
||||
</div>
|
||||
<div class="node-label">
|
||||
服务器 2
|
||||
</div>
|
||||
</div>
|
||||
<div class="server-node">
|
||||
<div class="node-icon">🖥️</div>
|
||||
<div class="node-label">服务器 3</div>
|
||||
<div class="node-icon">
|
||||
🖥️
|
||||
</div>
|
||||
<div class="node-label">
|
||||
服务器 3
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 完整架构 -->
|
||||
<div v-if="currentView === 3" class="full-architecture">
|
||||
<div
|
||||
v-if="currentView === 3"
|
||||
class="full-architecture"
|
||||
>
|
||||
<div class="user-nodes">
|
||||
<div class="user-node">
|
||||
<div class="node-icon">👤</div>
|
||||
<div class="node-label">用户</div>
|
||||
<div class="node-icon">
|
||||
👤
|
||||
</div>
|
||||
<div class="node-label">
|
||||
用户
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="arrow-down">↓</div>
|
||||
|
||||
<div class="dns-node">
|
||||
<div class="node-icon">🔍</div>
|
||||
<div class="node-label">DNS</div>
|
||||
<div class="arrow-down">
|
||||
↓
|
||||
</div>
|
||||
|
||||
<div class="arrow-down">↓</div>
|
||||
<div class="dns-node">
|
||||
<div class="node-icon">
|
||||
🔍
|
||||
</div>
|
||||
<div class="node-label">
|
||||
DNS
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="arrow-down">
|
||||
↓
|
||||
</div>
|
||||
|
||||
<div class="cdn-lb-row">
|
||||
<div class="cdn-node">
|
||||
<div class="node-icon">📡</div>
|
||||
<div class="node-label">CDN</div>
|
||||
<div class="node-icon">
|
||||
📡
|
||||
</div>
|
||||
<div class="node-label">
|
||||
CDN
|
||||
</div>
|
||||
</div>
|
||||
<div class="lb-node">
|
||||
<div class="node-icon">⚖️</div>
|
||||
<div class="node-label">LB</div>
|
||||
<div class="node-icon">
|
||||
⚖️
|
||||
</div>
|
||||
<div class="node-label">
|
||||
LB
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="arrow-down">↓</div>
|
||||
<div class="arrow-down">
|
||||
↓
|
||||
</div>
|
||||
|
||||
<div class="server-cluster">
|
||||
<div class="server-node">
|
||||
<div class="node-icon">🖥️</div>
|
||||
<div class="node-label">Web 1</div>
|
||||
<div class="node-icon">
|
||||
🖥️
|
||||
</div>
|
||||
<div class="node-label">
|
||||
Web 1
|
||||
</div>
|
||||
</div>
|
||||
<div class="server-node">
|
||||
<div class="node-icon">🖥️</div>
|
||||
<div class="node-label">Web 2</div>
|
||||
<div class="node-icon">
|
||||
🖥️
|
||||
</div>
|
||||
<div class="node-label">
|
||||
Web 2
|
||||
</div>
|
||||
</div>
|
||||
<div class="server-node">
|
||||
<div class="node-icon">💾</div>
|
||||
<div class="node-label">Database</div>
|
||||
<div class="node-icon">
|
||||
💾
|
||||
</div>
|
||||
<div class="node-label">
|
||||
Database
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -168,54 +294,74 @@
|
||||
</div>
|
||||
|
||||
<div class="info-cards">
|
||||
<div class="info-card" v-if="currentView === 0">
|
||||
<div class="card-title">🌐 域名 (Domain)</div>
|
||||
<div
|
||||
v-if="currentView === 0"
|
||||
class="info-card"
|
||||
>
|
||||
<div class="card-title">
|
||||
🌐 域名 (Domain)
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<strong>什么是域名?</strong>
|
||||
<br />域名是网站的地址,如 example.com,便于记忆和访问。 <br /><br />
|
||||
<br>域名是网站的地址,如 example.com,便于记忆和访问。 <br><br>
|
||||
<strong>域名注册</strong>
|
||||
<br />• 注册商:GoDaddy、Namecheap、阿里云 <br />•
|
||||
选择后缀:.com、.cn、.org、.io <br />• 价格:$10-50/年
|
||||
<br>• 注册商:GoDaddy、Namecheap、阿里云 <br>•
|
||||
选择后缀:.com、.cn、.org、.io <br>• 价格:$10-50/年
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card" v-if="currentView === 1">
|
||||
<div class="card-title">📡 CDN (内容分发网络)</div>
|
||||
<div
|
||||
v-if="currentView === 1"
|
||||
class="info-card"
|
||||
>
|
||||
<div class="card-title">
|
||||
📡 CDN (内容分发网络)
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<strong>什么是 CDN?</strong>
|
||||
<br />将内容缓存到全球各地的节点,用户就近访问。 <br /><br />
|
||||
<br>将内容缓存到全球各地的节点,用户就近访问。 <br><br>
|
||||
<strong>优势</strong>
|
||||
<br />• 加速访问:就近获取内容 <br />• 减轻负载:减少源站压力 <br />•
|
||||
提高可用性:节点故障自动切换 <br /><br />
|
||||
<br>• 加速访问:就近获取内容 <br>• 减轻负载:减少源站压力 <br>•
|
||||
提高可用性:节点故障自动切换 <br><br>
|
||||
<strong>常见 CDN</strong>
|
||||
<br />• Cloudflare、AWS CloudFront、阿里云 CDN
|
||||
<br>• Cloudflare、AWS CloudFront、阿里云 CDN
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card" v-if="currentView === 2">
|
||||
<div class="card-title">⚖️ 负载均衡 (Load Balancer)</div>
|
||||
<div
|
||||
v-if="currentView === 2"
|
||||
class="info-card"
|
||||
>
|
||||
<div class="card-title">
|
||||
⚖️ 负载均衡 (Load Balancer)
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<strong>什么是负载均衡?</strong>
|
||||
<br />将请求分发到多台服务器,提高并发能力。 <br /><br />
|
||||
<br>将请求分发到多台服务器,提高并发能力。 <br><br>
|
||||
<strong>负载均衡算法</strong>
|
||||
<br />• 轮询 (Round Robin) <br />• 最少连接 (Least Connections)
|
||||
<br />• IP 哈希 (IP Hash) <br /><br />
|
||||
<br>• 轮询 (Round Robin) <br>• 最少连接 (Least Connections)
|
||||
<br>• IP 哈希 (IP Hash) <br><br>
|
||||
<strong>常见工具</strong>
|
||||
<br />• Nginx、HAProxy、AWS ELB
|
||||
<br>• Nginx、HAProxy、AWS ELB
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card" v-if="currentView === 3">
|
||||
<div class="card-title">🏗️ 完整部署架构</div>
|
||||
<div
|
||||
v-if="currentView === 3"
|
||||
class="info-card"
|
||||
>
|
||||
<div class="card-title">
|
||||
🏗️ 完整部署架构
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<strong>现代 Web 应用架构</strong>
|
||||
<br /><br />
|
||||
<br><br>
|
||||
1. 用户通过域名访问
|
||||
<br />2. DNS 解析到 CDN 或负载均衡器 <br />3. CDN 缓存静态资源
|
||||
<br />4. 负载均衡器分发请求 <br />5. Web 服务器处理动态请求 <br />6.
|
||||
数据库存储持久化数据 <br /><br />
|
||||
<br>2. DNS 解析到 CDN 或负载均衡器 <br>3. CDN 缓存静态资源
|
||||
<br>4. 负载均衡器分发请求 <br>5. Web 服务器处理动态请求 <br>6.
|
||||
数据库存储持久化数据 <br><br>
|
||||
<strong>监控和运维</strong>
|
||||
<br />• 日志收集、性能监控、自动备份
|
||||
<br>• 日志收集、性能监控、自动备份
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<p class="why-desc-zh">
|
||||
你知道店铺名字叫 "Shop.com",但快递员需要知道具体的经纬度坐标 (IP 地址)
|
||||
才能送达。
|
||||
<br />
|
||||
<br>
|
||||
DNS 就像是<strong>地图导航</strong>,输入店名,它告诉你具体的坐标。
|
||||
</p>
|
||||
</div>
|
||||
@@ -15,22 +15,36 @@
|
||||
<div class="demo-stage">
|
||||
<div class="input-area">
|
||||
<span class="label">店铺名称 (域名)</span>
|
||||
<div class="fake-input">shop.com</div>
|
||||
<div class="fake-input">
|
||||
shop.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">正在查找 shop.com 的位置...</div>
|
||||
<div class="arrow-down">
|
||||
⬇️
|
||||
</div>
|
||||
<div class="dns-box">
|
||||
<div class="icon">
|
||||
🧭
|
||||
</div>
|
||||
<div class="title">
|
||||
DNS (地图导航)
|
||||
</div>
|
||||
<div class="desc">
|
||||
正在查找 shop.com 的位置...
|
||||
</div>
|
||||
</div>
|
||||
<div class="arrow-down">
|
||||
⬇️
|
||||
</div>
|
||||
<div class="arrow-down">⬇️</div>
|
||||
</div>
|
||||
|
||||
<div class="output-area">
|
||||
<span class="label">GPS 坐标 (IP 地址)</span>
|
||||
<div class="fake-output">93.184.216.34</div>
|
||||
<div class="fake-output">
|
||||
93.184.216.34
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -9,21 +9,34 @@
|
||||
<div class="left-panel">
|
||||
<div class="operations">
|
||||
<div class="op-group">
|
||||
<div class="op-label">修改内容</div>
|
||||
<div class="op-label">
|
||||
修改内容
|
||||
</div>
|
||||
<div class="op-row">
|
||||
<input v-model="titleText" placeholder="输入标题" class="input" />
|
||||
<button @click="updateTitle" class="btn">更新标题</button>
|
||||
<input
|
||||
v-model="titleText"
|
||||
placeholder="输入标题"
|
||||
class="input"
|
||||
>
|
||||
<button
|
||||
class="btn"
|
||||
@click="updateTitle"
|
||||
>
|
||||
更新标题
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="op-group">
|
||||
<div class="op-label">修改样式</div>
|
||||
<div class="op-label">
|
||||
修改样式
|
||||
</div>
|
||||
<div class="op-row">
|
||||
<button
|
||||
v-for="s in styles"
|
||||
:key="s.id"
|
||||
@click="currentStyle = s.id"
|
||||
:class="['btn-sm', { active: currentStyle === s.id }]"
|
||||
@click="currentStyle = s.id"
|
||||
>
|
||||
{{ s.label }}
|
||||
</button>
|
||||
@@ -31,59 +44,124 @@
|
||||
</div>
|
||||
|
||||
<div class="op-group">
|
||||
<div class="op-label">添加/删除元素</div>
|
||||
<div class="op-label">
|
||||
添加/删除元素
|
||||
</div>
|
||||
<div class="op-row">
|
||||
<button @click="addItem" class="btn">添加项目</button>
|
||||
<button @click="removeLastItem" class="btn btn-danger">删除最后</button>
|
||||
<button
|
||||
class="btn"
|
||||
@click="addItem"
|
||||
>
|
||||
添加项目
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-danger"
|
||||
@click="removeLastItem"
|
||||
>
|
||||
删除最后
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="preview-card" :class="currentStyle">
|
||||
<h2 class="card-title">{{ titleText || '点击按钮更新标题' }}</h2>
|
||||
<p class="card-desc">这是一个演示 DOM 操作的卡片区域。</p>
|
||||
<div
|
||||
class="preview-card"
|
||||
:class="currentStyle"
|
||||
>
|
||||
<h2 class="card-title">
|
||||
{{ titleText || '点击按钮更新标题' }}
|
||||
</h2>
|
||||
<p class="card-desc">
|
||||
这是一个演示 DOM 操作的卡片区域。
|
||||
</p>
|
||||
<ul class="card-list">
|
||||
<li v-for="(item, i) in items" :key="i">{{ item }}</li>
|
||||
<li v-if="items.length === 0" class="empty">(列表为空)</li>
|
||||
<li
|
||||
v-for="(item, i) in items"
|
||||
:key="i"
|
||||
>
|
||||
{{ item }}
|
||||
</li>
|
||||
<li
|
||||
v-if="items.length === 0"
|
||||
class="empty"
|
||||
>
|
||||
(列表为空)
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="right-panel">
|
||||
<div class="code-block">
|
||||
<div class="code-title">对应的 JavaScript 代码</div>
|
||||
<div class="code-title">
|
||||
对应的 JavaScript 代码
|
||||
</div>
|
||||
<div class="code-content">
|
||||
<template v-if="lastOp === 'title'">
|
||||
<div class="line comment">// 修改文本内容</div>
|
||||
<div class="line">const el = document.querySelector('.card-title')</div>
|
||||
<div class="line">el.textContent = '{{ titleText }}'</div>
|
||||
<div class="line comment">
|
||||
// 修改文本内容
|
||||
</div>
|
||||
<div class="line">
|
||||
const el = document.querySelector('.card-title')
|
||||
</div>
|
||||
<div class="line">
|
||||
el.textContent = '{{ titleText }}'
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="lastOp === 'style'">
|
||||
<div class="line comment">// 切换 CSS 类</div>
|
||||
<div class="line">const card = document.querySelector('.preview-card')</div>
|
||||
<div class="line">card.className = 'preview-card {{ currentStyle }}'</div>
|
||||
<div class="line comment">
|
||||
// 切换 CSS 类
|
||||
</div>
|
||||
<div class="line">
|
||||
const card = document.querySelector('.preview-card')
|
||||
</div>
|
||||
<div class="line">
|
||||
card.className = 'preview-card {{ currentStyle }}'
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="lastOp === 'add'">
|
||||
<div class="line comment">// 创建并添加新元素</div>
|
||||
<div class="line">const list = document.querySelector('.card-list')</div>
|
||||
<div class="line">const li = document.createElement('li')</div>
|
||||
<div class="line">li.textContent = '新项目 {{ items.length }}'</div>
|
||||
<div class="line">list.appendChild(li)</div>
|
||||
<div class="line comment">
|
||||
// 创建并添加新元素
|
||||
</div>
|
||||
<div class="line">
|
||||
const list = document.querySelector('.card-list')
|
||||
</div>
|
||||
<div class="line">
|
||||
const li = document.createElement('li')
|
||||
</div>
|
||||
<div class="line">
|
||||
li.textContent = '新项目 {{ items.length }}'
|
||||
</div>
|
||||
<div class="line">
|
||||
list.appendChild(li)
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="lastOp === 'remove'">
|
||||
<div class="line comment">// 删除最后一个元素</div>
|
||||
<div class="line">const list = document.querySelector('.card-list')</div>
|
||||
<div class="line">const last = list.lastElementChild</div>
|
||||
<div class="line">if (last) last.remove()</div>
|
||||
<div class="line comment">
|
||||
// 删除最后一个元素
|
||||
</div>
|
||||
<div class="line">
|
||||
const list = document.querySelector('.card-list')
|
||||
</div>
|
||||
<div class="line">
|
||||
const last = list.lastElementChild
|
||||
</div>
|
||||
<div class="line">
|
||||
if (last) last.remove()
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="line comment">// 点击左侧按钮查看对应代码</div>
|
||||
<div class="line comment">
|
||||
// 点击左侧按钮查看对应代码
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="methods-card">
|
||||
<div class="methods-title">常用 DOM 方法</div>
|
||||
<div class="methods-title">
|
||||
常用 DOM 方法
|
||||
</div>
|
||||
<div class="methods-list">
|
||||
<div class="method">
|
||||
<code>querySelector()</code>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="frontend-evolution-demo">
|
||||
<!-- Modern Timeline -->
|
||||
<div class="timeline-container">
|
||||
<div class="timeline-track"></div>
|
||||
<div class="timeline-track" />
|
||||
<button
|
||||
v-for="(stage, index) in stages"
|
||||
:key="index"
|
||||
@@ -14,7 +14,7 @@
|
||||
@click="currentStage = index"
|
||||
>
|
||||
<div class="node-dot">
|
||||
<div class="inner-dot"></div>
|
||||
<div class="inner-dot" />
|
||||
</div>
|
||||
<div class="node-content">
|
||||
<span class="year-badge">{{ stage.year }}</span>
|
||||
@@ -24,13 +24,17 @@
|
||||
</div>
|
||||
|
||||
<div class="content-wrapper">
|
||||
<transition name="fade-slide" mode="out-in">
|
||||
<div :key="currentStage" class="stage-content">
|
||||
<transition
|
||||
name="fade-slide"
|
||||
mode="out-in"
|
||||
>
|
||||
<div
|
||||
:key="currentStage"
|
||||
class="stage-content"
|
||||
>
|
||||
<div class="header-section">
|
||||
<h3>
|
||||
<span class="stage-index"
|
||||
>{{ indexToRoman(currentStage + 1) }}.</span
|
||||
>
|
||||
<span class="stage-index">{{ indexToRoman(currentStage + 1) }}.</span>
|
||||
{{ stages[currentStage].title }}
|
||||
</h3>
|
||||
<p>{{ stages[currentStage].desc }}</p>
|
||||
@@ -41,9 +45,9 @@
|
||||
<div class="mac-window code-window">
|
||||
<div class="window-bar">
|
||||
<div class="traffic-lights">
|
||||
<span class="light red"></span>
|
||||
<span class="light yellow"></span>
|
||||
<span class="light green"></span>
|
||||
<span class="light red" />
|
||||
<span class="light yellow" />
|
||||
<span class="light green" />
|
||||
</div>
|
||||
<div class="window-title">
|
||||
{{ stages[currentStage].codeTitle }}
|
||||
@@ -57,30 +61,45 @@
|
||||
<!-- Diagram View -->
|
||||
<div class="mac-window diagram-window">
|
||||
<div class="window-bar">
|
||||
<div class="window-title">Architecture Pattern</div>
|
||||
<div class="window-title">
|
||||
Architecture Pattern
|
||||
</div>
|
||||
</div>
|
||||
<div class="diagram-canvas">
|
||||
<!-- Stage 0: Static -->
|
||||
<div v-if="currentStage === 0" class="diagram static">
|
||||
<div
|
||||
v-if="currentStage === 0"
|
||||
class="diagram static"
|
||||
>
|
||||
<div class="flow-stack">
|
||||
<div class="concept-box html">
|
||||
<span class="icon">📄</span> HTML (Content)
|
||||
</div>
|
||||
<div class="flow-arrow">↓</div>
|
||||
<div class="flow-arrow">
|
||||
↓
|
||||
</div>
|
||||
<div class="concept-box browser">
|
||||
<span class="icon">🌍</span> Browser (Display)
|
||||
</div>
|
||||
</div>
|
||||
<div class="side-note">Server sends complete HTML</div>
|
||||
<div class="side-note">
|
||||
Server sends complete HTML
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Stage 1: jQuery -->
|
||||
<div v-if="currentStage === 1" class="diagram jquery">
|
||||
<div
|
||||
v-if="currentStage === 1"
|
||||
class="diagram jquery"
|
||||
>
|
||||
<div class="concept-box dom">
|
||||
<span class="icon">🌳</span> DOM Tree
|
||||
</div>
|
||||
<div class="chaos-arrows">
|
||||
<svg viewBox="0 0 100 60" class="chaos-svg">
|
||||
<svg
|
||||
viewBox="0 0 100 60"
|
||||
class="chaos-svg"
|
||||
>
|
||||
<path
|
||||
d="M10,10 Q50,5 90,10"
|
||||
class="arrow-path"
|
||||
@@ -115,45 +134,70 @@
|
||||
refY="3.5"
|
||||
orient="auto"
|
||||
>
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#666" />
|
||||
<polygon
|
||||
points="0 0, 10 3.5, 0 7"
|
||||
fill="#666"
|
||||
/>
|
||||
</marker>
|
||||
</defs>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<!-- Stage 2: MVC -->
|
||||
<div v-if="currentStage === 2" class="diagram mvc">
|
||||
<div
|
||||
v-if="currentStage === 2"
|
||||
class="diagram mvc"
|
||||
>
|
||||
<div class="mvc-triangle">
|
||||
<div class="concept-box model">Model</div>
|
||||
<div class="concept-box view">View</div>
|
||||
<div class="concept-box controller">Controller</div>
|
||||
<div class="concept-box model">
|
||||
Model
|
||||
</div>
|
||||
<div class="concept-box view">
|
||||
View
|
||||
</div>
|
||||
<div class="concept-box controller">
|
||||
Controller
|
||||
</div>
|
||||
|
||||
<!-- Connecting Lines -->
|
||||
<div class="line m-v"></div>
|
||||
<div class="line v-c"></div>
|
||||
<div class="line c-m"></div>
|
||||
<div class="line m-v" />
|
||||
<div class="line v-c" />
|
||||
<div class="line c-m" />
|
||||
</div>
|
||||
<div class="mvc-desc">
|
||||
Two-way Binding
|
||||
</div>
|
||||
<div class="mvc-desc">Two-way Binding</div>
|
||||
</div>
|
||||
|
||||
<!-- Stage 3: Component -->
|
||||
<div v-if="currentStage === 3" class="diagram component">
|
||||
<div
|
||||
v-if="currentStage === 3"
|
||||
class="diagram component"
|
||||
>
|
||||
<div class="comp-structure">
|
||||
<div class="comp-box root">
|
||||
<span class="comp-label">App</span>
|
||||
<div class="comp-children">
|
||||
<div class="comp-box header">Header</div>
|
||||
<div class="comp-box header">
|
||||
Header
|
||||
</div>
|
||||
<div class="comp-box list">
|
||||
ProductList
|
||||
<div class="comp-children row">
|
||||
<div class="comp-box item">Item</div>
|
||||
<div class="comp-box item">Item</div>
|
||||
<div class="comp-box item">
|
||||
Item
|
||||
</div>
|
||||
<div class="comp-box item">
|
||||
Item
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flow-pill">State ➔ UI = f(State)</div>
|
||||
<div class="flow-pill">
|
||||
State ➔ UI = f(State)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,14 +3,26 @@
|
||||
<div class="browser-frame">
|
||||
<!-- Address Bar (Simplified) -->
|
||||
<div class="address-bar">
|
||||
<select v-model="method" class="method-select" :disabled="loading">
|
||||
<select
|
||||
v-model="method"
|
||||
class="method-select"
|
||||
:disabled="loading"
|
||||
>
|
||||
<option>GET</option>
|
||||
<option>POST</option>
|
||||
<option>PUT</option>
|
||||
<option>DELETE</option>
|
||||
</select>
|
||||
<input v-model="path" class="url-input" :disabled="loading" />
|
||||
<button @click="sendRequest" :disabled="loading" class="send-btn">
|
||||
<input
|
||||
v-model="path"
|
||||
class="url-input"
|
||||
:disabled="loading"
|
||||
>
|
||||
<button
|
||||
:disabled="loading"
|
||||
class="send-btn"
|
||||
@click="sendRequest"
|
||||
>
|
||||
{{ loading ? '...' : t.send }}
|
||||
</button>
|
||||
</div>
|
||||
@@ -25,22 +37,33 @@
|
||||
<span>{{ t.cols.time }}</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="requestSent"
|
||||
class="log-row"
|
||||
:class="{ active: requestSent, selected: true }"
|
||||
v-if="requestSent"
|
||||
>
|
||||
<span class="col-name">{{ path.split('/').pop() || 'index' }}</span>
|
||||
<span class="col-status" :class="statusClass">{{
|
||||
<span
|
||||
class="col-status"
|
||||
:class="statusClass"
|
||||
>{{
|
||||
responseStatus
|
||||
}}</span>
|
||||
<span class="col-type">document</span>
|
||||
<span class="col-time">{{ loading ? 'Pending' : '45ms' }}</span>
|
||||
</div>
|
||||
<div v-else class="empty-state">{{ t.noRequests }}</div>
|
||||
<div
|
||||
v-else
|
||||
class="empty-state"
|
||||
>
|
||||
{{ t.noRequests }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Details Panel (Right) -->
|
||||
<div class="details-panel" v-if="requestSent">
|
||||
<div
|
||||
v-if="requestSent"
|
||||
class="details-panel"
|
||||
>
|
||||
<div class="tabs">
|
||||
<button
|
||||
v-for="tabKey in ['headers', 'response', 'preview']"
|
||||
@@ -54,9 +77,14 @@
|
||||
|
||||
<div class="tab-content">
|
||||
<!-- Headers Tab -->
|
||||
<div v-if="activeTab === 'headers'" class="headers-view">
|
||||
<div
|
||||
v-if="activeTab === 'headers'"
|
||||
class="headers-view"
|
||||
>
|
||||
<div class="section">
|
||||
<div class="section-title">{{ t.general }}</div>
|
||||
<div class="section-title">
|
||||
{{ t.general }}
|
||||
</div>
|
||||
<div class="kv-row">
|
||||
<span class="key">{{ t.requestUrl }}:</span>
|
||||
<span class="value">https://api.example.com{{ path }}</span>
|
||||
@@ -68,17 +96,22 @@
|
||||
<div class="kv-row">
|
||||
<span class="key">{{ t.statusCode }}:</span>
|
||||
<span class="value">
|
||||
<span class="status-dot" :class="statusClass"></span>
|
||||
<span
|
||||
class="status-dot"
|
||||
:class="statusClass"
|
||||
/>
|
||||
{{ responseStatus || '...' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section">
|
||||
<div class="section-title">{{ t.responseHeaders }}</div>
|
||||
<div class="section-title">
|
||||
{{ t.responseHeaders }}
|
||||
</div>
|
||||
<div
|
||||
class="kv-row"
|
||||
v-for="(val, key) in responseHeaders"
|
||||
:key="key"
|
||||
class="kv-row"
|
||||
>
|
||||
<span class="key">{{ key }}:</span>
|
||||
<span class="value">{{ val }}</span>
|
||||
@@ -87,24 +120,36 @@
|
||||
</div>
|
||||
|
||||
<!-- Response Tab -->
|
||||
<div v-if="activeTab === 'response'" class="code-view">
|
||||
<div
|
||||
v-if="activeTab === 'response'"
|
||||
class="code-view"
|
||||
>
|
||||
<pre>{{ responseBody }}</pre>
|
||||
</div>
|
||||
|
||||
<!-- Preview Tab -->
|
||||
<div v-if="activeTab === 'preview'" class="preview-view">
|
||||
<div
|
||||
v-if="activeTab === 'preview'"
|
||||
class="preview-view"
|
||||
>
|
||||
<div
|
||||
v-if="method === 'GET'"
|
||||
class="html-preview"
|
||||
v-html="responseBody"
|
||||
></div>
|
||||
<div v-else class="json-preview">
|
||||
/>
|
||||
<div
|
||||
v-else
|
||||
class="json-preview"
|
||||
>
|
||||
JSON Data: {{ responseBody }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="details-placeholder">
|
||||
<div
|
||||
v-else
|
||||
class="details-placeholder"
|
||||
>
|
||||
{{ t.placeholder }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+33
-9
@@ -16,30 +16,46 @@
|
||||
</div>
|
||||
<div class="code-preview">
|
||||
<code>
|
||||
// 手动操作 DOM<br />
|
||||
$('#count').text(val);<br />
|
||||
// 手动操作 DOM<br>
|
||||
$('#count').text(val);<br>
|
||||
if (val > 5) $('#msg').show();
|
||||
</code>
|
||||
</div>
|
||||
<div class="interactive-area">
|
||||
<div class="output-box">
|
||||
Count: <span id="imp-count-display">{{ impCount }}</span>
|
||||
<div v-show="impShowMsg" class="warning-msg">⚠️ Count is high!</div>
|
||||
<div
|
||||
v-show="impShowMsg"
|
||||
class="warning-msg"
|
||||
>
|
||||
⚠️ Count is high!
|
||||
</div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button @click="impIncrement" class="btn">Step 1: Value++</button>
|
||||
<button @click="impUpdateText" class="btn" :disabled="!impChanged">
|
||||
<button
|
||||
class="btn"
|
||||
@click="impIncrement"
|
||||
>
|
||||
Step 1: Value++
|
||||
</button>
|
||||
<button
|
||||
class="btn"
|
||||
:disabled="!impChanged"
|
||||
@click="impUpdateText"
|
||||
>
|
||||
Step 2: Update Text
|
||||
</button>
|
||||
<button
|
||||
@click="impCheckState"
|
||||
class="btn"
|
||||
:disabled="!impTextUpdated"
|
||||
@click="impCheckState"
|
||||
>
|
||||
Step 3: Check Logic
|
||||
</button>
|
||||
</div>
|
||||
<div class="status-log">{{ impStatus }}</div>
|
||||
<div class="status-log">
|
||||
{{ impStatus }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -59,10 +75,18 @@
|
||||
<div class="interactive-area">
|
||||
<div class="output-box">
|
||||
Count: <span>{{ decCount }}</span>
|
||||
<div v-if="decCount > 5" class="warning-msg">⚠️ Count is high!</div>
|
||||
<div
|
||||
v-if="decCount > 5"
|
||||
class="warning-msg"
|
||||
>
|
||||
⚠️ Count is high!
|
||||
</div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button @click="decIncrement" class="btn primary">
|
||||
<button
|
||||
class="btn primary"
|
||||
@click="decIncrement"
|
||||
>
|
||||
Value++ (Auto Render)
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
<template>
|
||||
<div class="jq-demo">
|
||||
<div class="header">
|
||||
<div class="title">什么是 jQuery?用“购物车数量”秒懂</div>
|
||||
<div class="title">
|
||||
什么是 jQuery?用“购物车数量”秒懂
|
||||
</div>
|
||||
<div class="subtitle">
|
||||
左边:像 jQuery 一样手动改页面(容易漏)。右边:像 Vue/React
|
||||
一样只改状态。
|
||||
@@ -15,48 +17,83 @@
|
||||
<div class="panes">
|
||||
<!-- jQuery-like -->
|
||||
<div class="pane">
|
||||
<div class="pane-title">jQuery 思路:到处改 DOM</div>
|
||||
<div class="pane-title">
|
||||
jQuery 思路:到处改 DOM
|
||||
</div>
|
||||
<div class="mock-app">
|
||||
<div class="topbar">
|
||||
<span>🛒 角标:</span>
|
||||
<span class="badge" :class="{ wrong: jqBadgeWrong }">{{
|
||||
<span
|
||||
class="badge"
|
||||
:class="{ wrong: jqBadgeWrong }"
|
||||
>{{
|
||||
jqBadge
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="row">
|
||||
购物车页数量:
|
||||
<span class="num" :class="{ wrong: jqPageWrong }">{{
|
||||
<span
|
||||
class="num"
|
||||
:class="{ wrong: jqPageWrong }"
|
||||
>{{
|
||||
jqPage
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
结算按钮:
|
||||
<button class="checkout">去结算 ({{ jqButtonLabel }})</button>
|
||||
<button class="checkout">
|
||||
去结算 ({{ jqButtonLabel }})
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<div class="control-title">模拟“你写的命令”</div>
|
||||
<div class="control-title">
|
||||
模拟“你写的命令”
|
||||
</div>
|
||||
<div class="btns">
|
||||
<button @click="jqIncreaseData">数据 +1(但还没改页面)</button>
|
||||
<button @click="jqUpdateBadge">改角标</button>
|
||||
<button @click="jqUpdateCartPage">改购物车页</button>
|
||||
<button @click="jqUpdateCheckoutButton">改结算按钮</button>
|
||||
<button @click="jqIncreaseData">
|
||||
数据 +1(但还没改页面)
|
||||
</button>
|
||||
<button @click="jqUpdateBadge">
|
||||
改角标
|
||||
</button>
|
||||
<button @click="jqUpdateCartPage">
|
||||
改购物车页
|
||||
</button>
|
||||
<button @click="jqUpdateCheckoutButton">
|
||||
改结算按钮
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="hint" :class="{ danger: jqInconsistent }">
|
||||
<div
|
||||
class="hint"
|
||||
:class="{ danger: jqInconsistent }"
|
||||
>
|
||||
{{ jqHint }}
|
||||
</div>
|
||||
|
||||
<div class="log">
|
||||
<div class="log-title">命令日志</div>
|
||||
<div v-if="jqLogs.length === 0" class="log-empty">
|
||||
<div class="log-title">
|
||||
命令日志
|
||||
</div>
|
||||
<div
|
||||
v-if="jqLogs.length === 0"
|
||||
class="log-empty"
|
||||
>
|
||||
(还没有操作)
|
||||
</div>
|
||||
<div v-else class="log-list">
|
||||
<div v-for="(l, idx) in jqLogs" :key="idx" class="log-item">
|
||||
<div
|
||||
v-else
|
||||
class="log-list"
|
||||
>
|
||||
<div
|
||||
v-for="(l, idx) in jqLogs"
|
||||
:key="idx"
|
||||
class="log-item"
|
||||
>
|
||||
{{ l }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -66,7 +103,9 @@
|
||||
|
||||
<!-- State-driven -->
|
||||
<div class="pane">
|
||||
<div class="pane-title">Vue/React 思路:只改 State</div>
|
||||
<div class="pane-title">
|
||||
Vue/React 思路:只改 State
|
||||
</div>
|
||||
<div class="mock-app">
|
||||
<div class="topbar">
|
||||
<span>🛒 角标:</span>
|
||||
@@ -78,23 +117,39 @@
|
||||
</div>
|
||||
<div class="row">
|
||||
结算按钮:
|
||||
<button class="checkout">去结算 ({{ state }} 件)</button>
|
||||
<button class="checkout">
|
||||
去结算 ({{ state }} 件)
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<div class="control-title">你只需要做一件事</div>
|
||||
<div class="control-title">
|
||||
你只需要做一件事
|
||||
</div>
|
||||
<div class="btns">
|
||||
<button class="primary" @click="state = state + 1">state +1</button>
|
||||
<button class="secondary" @click="resetAll">重置</button>
|
||||
<button
|
||||
class="primary"
|
||||
@click="state = state + 1"
|
||||
>
|
||||
state +1
|
||||
</button>
|
||||
<button
|
||||
class="secondary"
|
||||
@click="resetAll"
|
||||
>
|
||||
重置
|
||||
</button>
|
||||
</div>
|
||||
<div class="hint ok">
|
||||
State 变了,界面三处会自动同步,不需要你“手动找 DOM 去改”。
|
||||
</div>
|
||||
|
||||
<div class="mini">
|
||||
<div class="mini-title">这里的两个新词</div>
|
||||
<div class="mini-title">
|
||||
这里的两个新词
|
||||
</div>
|
||||
<div class="mini-item">
|
||||
<strong>DOM</strong>:浏览器里的页面结构(按钮/文字/图片都在里面)
|
||||
</div>
|
||||
|
||||
@@ -8,21 +8,40 @@
|
||||
:class="{ active: selectedLayer === index }"
|
||||
@click="selectedLayer = index"
|
||||
>
|
||||
<div class="layer-number">{{ index + 1 }}</div>
|
||||
<div class="layer-content">
|
||||
<div class="layer-name">{{ layer.name }}</div>
|
||||
<div class="layer-english">{{ layer.english }}</div>
|
||||
<div class="layer-protocols">{{ layer.protocols }}</div>
|
||||
<div class="layer-number">
|
||||
{{ index + 1 }}
|
||||
</div>
|
||||
<div class="layer-content">
|
||||
<div class="layer-name">
|
||||
{{ layer.name }}
|
||||
</div>
|
||||
<div class="layer-english">
|
||||
{{ layer.english }}
|
||||
</div>
|
||||
<div class="layer-protocols">
|
||||
{{ layer.protocols }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="layer-icon">
|
||||
{{ layer.icon }}
|
||||
</div>
|
||||
<div class="layer-icon">{{ layer.icon }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layer-detail" v-if="selectedLayer !== null">
|
||||
<div class="detail-title">{{ layers[selectedLayer].name }}</div>
|
||||
<div class="detail-desc">{{ layers[selectedLayer].description }}</div>
|
||||
<div
|
||||
v-if="selectedLayer !== null"
|
||||
class="layer-detail"
|
||||
>
|
||||
<div class="detail-title">
|
||||
{{ layers[selectedLayer].name }}
|
||||
</div>
|
||||
<div class="detail-desc">
|
||||
{{ layers[selectedLayer].description }}
|
||||
</div>
|
||||
<div class="detail-functions">
|
||||
<div class="function-title">主要功能</div>
|
||||
<div class="function-title">
|
||||
主要功能
|
||||
</div>
|
||||
<div class="function-list">
|
||||
<div
|
||||
v-for="(func, index) in layers[selectedLayer].functions"
|
||||
@@ -34,7 +53,9 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-examples">
|
||||
<div class="example-title">常见设备</div>
|
||||
<div class="example-title">
|
||||
常见设备
|
||||
</div>
|
||||
<div class="example-list">
|
||||
<div
|
||||
v-for="(device, index) in layers[selectedLayer].devices"
|
||||
@@ -48,14 +69,27 @@
|
||||
</div>
|
||||
|
||||
<div class="data-flow">
|
||||
<div class="flow-title">数据封装过程(发送)</div>
|
||||
<div class="flow-title">
|
||||
数据封装过程(发送)
|
||||
</div>
|
||||
<div class="flow-steps">
|
||||
<div class="flow-step" v-for="(step, index) in 5" :key="index">
|
||||
<div class="step-label">{{ layers[4 - index].name }}</div>
|
||||
<div
|
||||
v-for="(step, index) in 5"
|
||||
:key="index"
|
||||
class="flow-step"
|
||||
>
|
||||
<div class="step-label">
|
||||
{{ layers[4 - index].name }}
|
||||
</div>
|
||||
<div class="step-box">
|
||||
<span class="box-label">{{ layers[4 - index].dataUnit }}</span>
|
||||
</div>
|
||||
<div class="step-arrow" v-if="index < 4">↓ 添加头部</div>
|
||||
<div
|
||||
v-if="index < 4"
|
||||
class="step-arrow"
|
||||
>
|
||||
↓ 添加头部
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
<template>
|
||||
<div class="network-troubleshooting">
|
||||
<div class="problem-selector">
|
||||
<div class="selector-title">选择问题类型</div>
|
||||
<div class="selector-title">
|
||||
选择问题类型
|
||||
</div>
|
||||
<div class="problem-list">
|
||||
<button
|
||||
v-for="(problem, index) in problems"
|
||||
@@ -16,16 +18,23 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="solution-panel" v-if="selectedProblem !== null">
|
||||
<div
|
||||
v-if="selectedProblem !== null"
|
||||
class="solution-panel"
|
||||
>
|
||||
<div class="solution-header">
|
||||
<div class="solution-title">{{ problems[selectedProblem].name }}</div>
|
||||
<div class="solution-title">
|
||||
{{ problems[selectedProblem].name }}
|
||||
</div>
|
||||
<div class="solution-desc">
|
||||
{{ problems[selectedProblem].description }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="solution-steps">
|
||||
<div class="steps-title">🔧 解决步骤</div>
|
||||
<div class="steps-title">
|
||||
🔧 解决步骤
|
||||
</div>
|
||||
<div class="steps-list">
|
||||
<div
|
||||
v-for="(step, index) in problems[selectedProblem].steps"
|
||||
@@ -34,13 +43,22 @@
|
||||
:class="{ completed: completedSteps.has(index) }"
|
||||
@click="toggleStep(index)"
|
||||
>
|
||||
<div class="step-number">{{ index + 1 }}</div>
|
||||
<div class="step-number">
|
||||
{{ index + 1 }}
|
||||
</div>
|
||||
<div class="step-content">
|
||||
<div class="step-action">{{ step.action }}</div>
|
||||
<div class="step-command" v-if="step.command">
|
||||
<div class="step-action">
|
||||
{{ step.action }}
|
||||
</div>
|
||||
<div
|
||||
v-if="step.command"
|
||||
class="step-command"
|
||||
>
|
||||
<code>{{ step.command }}</code>
|
||||
</div>
|
||||
<div class="step-explanation">{{ step.explanation }}</div>
|
||||
<div class="step-explanation">
|
||||
{{ step.explanation }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="step-check">
|
||||
{{ completedSteps.has(index) ? '✓' : '○' }}
|
||||
@@ -50,61 +68,89 @@
|
||||
</div>
|
||||
|
||||
<div class="related-tools">
|
||||
<div class="tools-title">🛠️ 相关工具</div>
|
||||
<div class="tools-title">
|
||||
🛠️ 相关工具
|
||||
</div>
|
||||
<div class="tools-list">
|
||||
<div
|
||||
v-for="(tool, index) in problems[selectedProblem].tools"
|
||||
:key="index"
|
||||
class="tool-item"
|
||||
>
|
||||
<div class="tool-name">{{ tool.name }}</div>
|
||||
<div class="tool-usage">{{ tool.usage }}</div>
|
||||
<div class="tool-name">
|
||||
{{ tool.name }}
|
||||
</div>
|
||||
<div class="tool-usage">
|
||||
{{ tool.usage }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="common-commands">
|
||||
<div class="commands-title">📋 常用诊断命令</div>
|
||||
<div class="commands-title">
|
||||
📋 常用诊断命令
|
||||
</div>
|
||||
<div class="commands-grid">
|
||||
<div class="command-card" v-for="(cmd, index) in commands" :key="index">
|
||||
<div class="command-name">{{ cmd.name }}</div>
|
||||
<div class="command-syntax">{{ cmd.syntax }}</div>
|
||||
<div class="command-desc">{{ cmd.description }}</div>
|
||||
<div
|
||||
v-for="(cmd, index) in commands"
|
||||
:key="index"
|
||||
class="command-card"
|
||||
>
|
||||
<div class="command-name">
|
||||
{{ cmd.name }}
|
||||
</div>
|
||||
<div class="command-syntax">
|
||||
{{ cmd.syntax }}
|
||||
</div>
|
||||
<div class="command-desc">
|
||||
{{ cmd.description }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="troubleshooting-tips">
|
||||
<div class="tips-title">💡 故障排查技巧</div>
|
||||
<div class="tips-title">
|
||||
💡 故障排查技巧
|
||||
</div>
|
||||
<div class="tips-list">
|
||||
<div class="tip-item">
|
||||
<div class="tip-number">1</div>
|
||||
<div class="tip-number">
|
||||
1
|
||||
</div>
|
||||
<div class="tip-content">
|
||||
<strong>从底层到顶层</strong>
|
||||
<br />物理层 → 链路层 → 网络层 → 传输层 → 应用层
|
||||
<br>物理层 → 链路层 → 网络层 → 传输层 → 应用层
|
||||
</div>
|
||||
</div>
|
||||
<div class="tip-item">
|
||||
<div class="tip-number">2</div>
|
||||
<div class="tip-number">
|
||||
2
|
||||
</div>
|
||||
<div class="tip-content">
|
||||
<strong>分层排查</strong>
|
||||
<br />先确定问题发生在哪一层,再针对性解决
|
||||
<br>先确定问题发生在哪一层,再针对性解决
|
||||
</div>
|
||||
</div>
|
||||
<div class="tip-item">
|
||||
<div class="tip-number">3</div>
|
||||
<div class="tip-number">
|
||||
3
|
||||
</div>
|
||||
<div class="tip-content">
|
||||
<strong>二分法定位</strong>
|
||||
<br />
|
||||
<br>
|
||||
ping 本机 → ping 网关 → ping 外网 → ping 域名
|
||||
</div>
|
||||
</div>
|
||||
<div class="tip-item">
|
||||
<div class="tip-number">4</div>
|
||||
<div class="tip-number">
|
||||
4
|
||||
</div>
|
||||
<div class="tip-content">
|
||||
<strong>查看日志</strong>
|
||||
<br />系统日志、应用日志、防火墙日志记录关键信息
|
||||
<br>系统日志、应用日志、防火墙日志记录关键信息
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,8 +5,12 @@
|
||||
<template>
|
||||
<div class="render-demo">
|
||||
<div class="header">
|
||||
<div class="title">渲染策略:CSR / SSR / SSG</div>
|
||||
<div class="subtitle">选择策略,观察首屏表现</div>
|
||||
<div class="title">
|
||||
渲染策略:CSR / SSR / SSG
|
||||
</div>
|
||||
<div class="subtitle">
|
||||
选择策略,观察首屏表现
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="options">
|
||||
@@ -23,20 +27,34 @@
|
||||
|
||||
<div class="cards">
|
||||
<div class="card">
|
||||
<div class="label">TTFB</div>
|
||||
<div class="value">{{ metrics.ttfb }} ms</div>
|
||||
<div class="label">
|
||||
TTFB
|
||||
</div>
|
||||
<div class="value">
|
||||
{{ metrics.ttfb }} ms
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="label">可交互时间</div>
|
||||
<div class="value">{{ metrics.tti }} ms</div>
|
||||
<div class="label">
|
||||
可交互时间
|
||||
</div>
|
||||
<div class="value">
|
||||
{{ metrics.tti }} ms
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="label">SEO 友好</div>
|
||||
<div class="value">{{ metrics.seo }}</div>
|
||||
<div class="label">
|
||||
SEO 友好
|
||||
</div>
|
||||
<div class="value">
|
||||
{{ metrics.seo }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hint">{{ metrics.note }}</div>
|
||||
<div class="hint">
|
||||
{{ metrics.note }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -5,8 +5,12 @@
|
||||
<template>
|
||||
<div class="responsive-demo">
|
||||
<div class="header">
|
||||
<div class="title">响应式布局:一套代码,多种屏幕</div>
|
||||
<div class="subtitle">拖动宽度,观察列数变化</div>
|
||||
<div class="title">
|
||||
响应式布局:一套代码,多种屏幕
|
||||
</div>
|
||||
<div class="subtitle">
|
||||
拖动宽度,观察列数变化
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
@@ -19,12 +23,24 @@
|
||||
min="320"
|
||||
max="1200"
|
||||
step="10"
|
||||
/>
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="preview" :style="{ width: viewportWidth + 'px' }">
|
||||
<div class="grid" :style="gridStyle">
|
||||
<div v-for="n in 6" :key="n" class="card">Card {{ n }}</div>
|
||||
<div
|
||||
class="preview"
|
||||
:style="{ width: viewportWidth + 'px' }"
|
||||
>
|
||||
<div
|
||||
class="grid"
|
||||
:style="gridStyle"
|
||||
>
|
||||
<div
|
||||
v-for="n in 6"
|
||||
:key="n"
|
||||
class="card"
|
||||
>
|
||||
Card {{ n }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -5,8 +5,12 @@
|
||||
<template>
|
||||
<div class="routing-demo">
|
||||
<div class="header">
|
||||
<div class="title">路由方式:整页刷新 vs 局部切换</div>
|
||||
<div class="subtitle">点击导航,感受体验差异</div>
|
||||
<div class="title">
|
||||
路由方式:整页刷新 vs 局部切换
|
||||
</div>
|
||||
<div class="subtitle">
|
||||
点击导航,感受体验差异
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mode-switch">
|
||||
@@ -27,14 +31,26 @@
|
||||
</div>
|
||||
|
||||
<div class="nav">
|
||||
<button v-for="page in pages" :key="page" @click="navigate(page)">
|
||||
<button
|
||||
v-for="page in pages"
|
||||
:key="page"
|
||||
@click="navigate(page)"
|
||||
>
|
||||
{{ page }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="screen">
|
||||
<div v-if="loading" class="loading">页面加载中...</div>
|
||||
<div v-else class="content">
|
||||
<div
|
||||
v-if="loading"
|
||||
class="loading"
|
||||
>
|
||||
页面加载中...
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="content"
|
||||
>
|
||||
当前页面:<strong>{{ currentPage }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -25,11 +25,20 @@
|
||||
<div class="row">
|
||||
<span class="label">常见位置</span><span>{{ current.scene }}</span>
|
||||
</div>
|
||||
<div class="row code-title">示例</div>
|
||||
<div class="row code-title">
|
||||
示例
|
||||
</div>
|
||||
<pre><code>{{ current.example }}</code></pre>
|
||||
<div class="row code-title">渲染效果</div>
|
||||
<div class="preview-box" v-html="current.example"></div>
|
||||
<div class="row tip">小贴士:{{ current.tip }}</div>
|
||||
<div class="row code-title">
|
||||
渲染效果
|
||||
</div>
|
||||
<div
|
||||
class="preview-box"
|
||||
v-html="current.example"
|
||||
/>
|
||||
<div class="row tip">
|
||||
小贴士:{{ current.tip }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -5,34 +5,58 @@
|
||||
<template>
|
||||
<div class="slice-demo">
|
||||
<div class="header">
|
||||
<div class="title">切图时代:请求数越多越慢</div>
|
||||
<div class="subtitle">调整切图数量,观察加载时间变化</div>
|
||||
<div class="title">
|
||||
切图时代:请求数越多越慢
|
||||
</div>
|
||||
<div class="subtitle">
|
||||
调整切图数量,观察加载时间变化
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<label>
|
||||
切图数量:<strong>{{ slices }}</strong> 张
|
||||
</label>
|
||||
<input v-model="slices" type="range" min="1" max="30" step="1" />
|
||||
<input
|
||||
v-model="slices"
|
||||
type="range"
|
||||
min="1"
|
||||
max="30"
|
||||
step="1"
|
||||
>
|
||||
<label class="toggle">
|
||||
<input v-model="useSprite" type="checkbox" />
|
||||
<input
|
||||
v-model="useSprite"
|
||||
type="checkbox"
|
||||
>
|
||||
合并雪碧图 (Sprite)
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="metrics">
|
||||
<div class="metric">
|
||||
<div class="label">总请求数</div>
|
||||
<div class="value">{{ totalRequests }}</div>
|
||||
<div class="label">
|
||||
总请求数
|
||||
</div>
|
||||
<div class="value">
|
||||
{{ totalRequests }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<div class="label">预计加载时间</div>
|
||||
<div class="value">{{ loadTime }} ms</div>
|
||||
<div class="label">
|
||||
预计加载时间
|
||||
</div>
|
||||
<div class="value">
|
||||
{{ loadTime }} ms
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bar">
|
||||
<div class="progress" :style="{ width: barWidth + '%' }"></div>
|
||||
<div
|
||||
class="progress"
|
||||
:style="{ width: barWidth + '%' }"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
<template>
|
||||
<div class="spa-state-demo">
|
||||
<div class="header">
|
||||
<div class="title">页面切换时,输入会不会丢?</div>
|
||||
<div class="title">
|
||||
页面切换时,输入会不会丢?
|
||||
</div>
|
||||
<div class="subtitle">
|
||||
同样点击“切换页面”,MPA 会像刷新一样清空;SPA 会保留状态
|
||||
</div>
|
||||
@@ -26,7 +28,12 @@
|
||||
>
|
||||
SPA(局部切换)
|
||||
</button>
|
||||
<button class="reset" @click="resetAll">重置</button>
|
||||
<button
|
||||
class="reset"
|
||||
@click="resetAll"
|
||||
>
|
||||
重置
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="app">
|
||||
@@ -43,8 +50,16 @@
|
||||
</div>
|
||||
|
||||
<div class="screen">
|
||||
<div v-if="loading" class="loading">加载中...</div>
|
||||
<div v-else class="content">
|
||||
<div
|
||||
v-if="loading"
|
||||
class="loading"
|
||||
>
|
||||
加载中...
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="content"
|
||||
>
|
||||
<div class="row">
|
||||
当前页面:<strong>{{ page }}</strong>
|
||||
</div>
|
||||
@@ -52,7 +67,11 @@
|
||||
<div class="form">
|
||||
<label>
|
||||
备注(模拟表单输入):
|
||||
<input v-model="note" type="text" placeholder="输入点东西试试" />
|
||||
<input
|
||||
v-model="note"
|
||||
type="text"
|
||||
placeholder="输入点东西试试"
|
||||
>
|
||||
</label>
|
||||
<div class="help">
|
||||
提示:切到别的页面再回来,看看这段文字还在不在。
|
||||
@@ -61,23 +80,39 @@
|
||||
|
||||
<div class="row">
|
||||
购物车数量(模拟状态):
|
||||
<button class="small" @click="cart = Math.max(0, cart - 1)">
|
||||
<button
|
||||
class="small"
|
||||
@click="cart = Math.max(0, cart - 1)"
|
||||
>
|
||||
-
|
||||
</button>
|
||||
<strong class="num">{{ cart }}</strong>
|
||||
<button class="small" @click="cart = cart + 1">+</button>
|
||||
<button
|
||||
class="small"
|
||||
@click="cart = cart + 1"
|
||||
>
|
||||
+
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="explain">
|
||||
<div class="card">
|
||||
<div class="label">你现在看到的现象</div>
|
||||
<div class="value">{{ explainText }}</div>
|
||||
<div class="label">
|
||||
你现在看到的现象
|
||||
</div>
|
||||
<div class="value">
|
||||
{{ explainText }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="label">背后的原因(一句话)</div>
|
||||
<div class="value">{{ reasonText }}</div>
|
||||
<div class="label">
|
||||
背后的原因(一句话)
|
||||
</div>
|
||||
<div class="value">
|
||||
{{ reasonText }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,88 +8,158 @@
|
||||
type="text"
|
||||
placeholder="例如: 192.168.1.0"
|
||||
class="ip-input"
|
||||
/>
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label class="input-label">子网掩码</label>
|
||||
<select v-model="cidr" class="cidr-select">
|
||||
<option v-for="n in 32" :key="n" :value="n">/{{ n }}</option>
|
||||
<select
|
||||
v-model="cidr"
|
||||
class="cidr-select"
|
||||
>
|
||||
<option
|
||||
v-for="n in 32"
|
||||
:key="n"
|
||||
:value="n"
|
||||
>
|
||||
/{{ n }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<button class="calculate-btn" @click="calculate">计算</button>
|
||||
<button
|
||||
class="calculate-btn"
|
||||
@click="calculate"
|
||||
>
|
||||
计算
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="results" v-if="results">
|
||||
<div
|
||||
v-if="results"
|
||||
class="results"
|
||||
>
|
||||
<div class="result-section">
|
||||
<div class="section-title">基本信息</div>
|
||||
<div class="section-title">
|
||||
基本信息
|
||||
</div>
|
||||
<div class="result-grid">
|
||||
<div class="result-item">
|
||||
<div class="result-label">网络地址</div>
|
||||
<div class="result-value">{{ results.network }}</div>
|
||||
<div class="result-label">
|
||||
网络地址
|
||||
</div>
|
||||
<div class="result-value">
|
||||
{{ results.network }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="result-item">
|
||||
<div class="result-label">广播地址</div>
|
||||
<div class="result-value">{{ results.broadcast }}</div>
|
||||
<div class="result-label">
|
||||
广播地址
|
||||
</div>
|
||||
<div class="result-value">
|
||||
{{ results.broadcast }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="result-item">
|
||||
<div class="result-label">子网掩码</div>
|
||||
<div class="result-value">{{ results.mask }}</div>
|
||||
<div class="result-label">
|
||||
子网掩码
|
||||
</div>
|
||||
<div class="result-value">
|
||||
{{ results.mask }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="result-item">
|
||||
<div class="result-label">可用主机数</div>
|
||||
<div class="result-value">{{ results.hosts }}</div>
|
||||
<div class="result-label">
|
||||
可用主机数
|
||||
</div>
|
||||
<div class="result-value">
|
||||
{{ results.hosts }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="result-section">
|
||||
<div class="section-title">IP 范围</div>
|
||||
<div class="section-title">
|
||||
IP 范围
|
||||
</div>
|
||||
<div class="range-display">
|
||||
<div class="range-item">
|
||||
<div class="range-label">起始 IP</div>
|
||||
<div class="range-value">{{ results.firstHost }}</div>
|
||||
<div class="range-label">
|
||||
起始 IP
|
||||
</div>
|
||||
<div class="range-value">
|
||||
{{ results.firstHost }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="range-arrow">
|
||||
→
|
||||
</div>
|
||||
<div class="range-arrow">→</div>
|
||||
<div class="range-item">
|
||||
<div class="range-label">结束 IP</div>
|
||||
<div class="range-value">{{ results.lastHost }}</div>
|
||||
<div class="range-label">
|
||||
结束 IP
|
||||
</div>
|
||||
<div class="range-value">
|
||||
{{ results.lastHost }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="result-section">
|
||||
<div class="section-title">二进制表示</div>
|
||||
<div class="section-title">
|
||||
二进制表示
|
||||
</div>
|
||||
<div class="binary-display">
|
||||
<div class="binary-row">
|
||||
<div class="binary-label">IP 地址</div>
|
||||
<div class="binary-value">{{ results.binaryIp }}</div>
|
||||
<div class="binary-label">
|
||||
IP 地址
|
||||
</div>
|
||||
<div class="binary-value">
|
||||
{{ results.binaryIp }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="binary-row">
|
||||
<div class="binary-label">子网掩码</div>
|
||||
<div class="binary-value">{{ results.binaryMask }}</div>
|
||||
<div class="binary-label">
|
||||
子网掩码
|
||||
</div>
|
||||
<div class="binary-value">
|
||||
{{ results.binaryMask }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="binary-row">
|
||||
<div class="binary-label">网络地址</div>
|
||||
<div class="binary-value">{{ results.binaryNetwork }}</div>
|
||||
<div class="binary-label">
|
||||
网络地址
|
||||
</div>
|
||||
<div class="binary-value">
|
||||
{{ results.binaryNetwork }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="result-section">
|
||||
<div class="section-title">子网类型</div>
|
||||
<div class="section-title">
|
||||
子网类型
|
||||
</div>
|
||||
<div class="subnet-info">
|
||||
<div class="info-tag" :class="getSubnetClass(cidr)">
|
||||
<div
|
||||
class="info-tag"
|
||||
:class="getSubnetClass(cidr)"
|
||||
>
|
||||
{{ getSubnetType(cidr) }}
|
||||
</div>
|
||||
<div class="info-desc">{{ getSubnetDescription(cidr) }}</div>
|
||||
<div class="info-desc">
|
||||
{{ getSubnetDescription(cidr) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="example-presets">
|
||||
<div class="presets-title">常见子网示例</div>
|
||||
<div class="presets-title">
|
||||
常见子网示例
|
||||
</div>
|
||||
<div class="presets-grid">
|
||||
<button
|
||||
v-for="(preset, index) in presets"
|
||||
@@ -103,7 +173,9 @@
|
||||
</div>
|
||||
|
||||
<div class="info-box">
|
||||
<div class="info-title">💡 子网划分知识点</div>
|
||||
<div class="info-title">
|
||||
💡 子网划分知识点
|
||||
</div>
|
||||
<div class="info-content">
|
||||
<div class="info-item">
|
||||
<strong>什么是子网?</strong>
|
||||
@@ -115,11 +187,11 @@
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<strong>常用子网掩码</strong>
|
||||
<br />
|
||||
<br>
|
||||
/8 = 255.0.0.0 (A 类网络)
|
||||
<br />
|
||||
<br>
|
||||
/16 = 255.255.0.0 (B 类网络)
|
||||
<br />
|
||||
<br>
|
||||
/24 = 255.255.255.0 (C 类网络)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -6,10 +6,20 @@
|
||||
<span :class="connectionStatus.toLowerCase()">{{ statusText }}</span>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button v-if="step === 0" @click="startHandshake" class="action-btn">
|
||||
<button
|
||||
v-if="step === 0"
|
||||
class="action-btn"
|
||||
@click="startHandshake"
|
||||
>
|
||||
{{ t.connect }}
|
||||
</button>
|
||||
<button v-else @click="reset" class="reset-btn">{{ t.reset }}</button>
|
||||
<button
|
||||
v-else
|
||||
class="reset-btn"
|
||||
@click="reset"
|
||||
>
|
||||
{{ t.reset }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -20,9 +30,17 @@
|
||||
<span class="icon">💻</span>
|
||||
<span class="name">{{ t.client }}</span>
|
||||
</div>
|
||||
<div class="line"></div>
|
||||
<div class="state-marker" :class="{ active: step >= 1 }">SYN_SENT</div>
|
||||
<div class="state-marker" :class="{ active: step >= 3 }">
|
||||
<div class="line" />
|
||||
<div
|
||||
class="state-marker"
|
||||
:class="{ active: step >= 1 }"
|
||||
>
|
||||
SYN_SENT
|
||||
</div>
|
||||
<div
|
||||
class="state-marker"
|
||||
:class="{ active: step >= 3 }"
|
||||
>
|
||||
ESTABLISHED
|
||||
</div>
|
||||
</div>
|
||||
@@ -32,9 +50,16 @@
|
||||
<!-- SYN Packet -->
|
||||
<div class="packet-track">
|
||||
<transition name="slide-right">
|
||||
<div v-if="showSyn" class="packet syn">
|
||||
<div class="packet-body">SYN</div>
|
||||
<div class="packet-detail">SEQ=0</div>
|
||||
<div
|
||||
v-if="showSyn"
|
||||
class="packet syn"
|
||||
>
|
||||
<div class="packet-body">
|
||||
SYN
|
||||
</div>
|
||||
<div class="packet-detail">
|
||||
SEQ=0
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
@@ -42,9 +67,16 @@
|
||||
<!-- SYN-ACK Packet -->
|
||||
<div class="packet-track reverse">
|
||||
<transition name="slide-left">
|
||||
<div v-if="showSynAck" class="packet syn-ack">
|
||||
<div class="packet-body">SYN-ACK</div>
|
||||
<div class="packet-detail">SEQ=0, ACK=1</div>
|
||||
<div
|
||||
v-if="showSynAck"
|
||||
class="packet syn-ack"
|
||||
>
|
||||
<div class="packet-body">
|
||||
SYN-ACK
|
||||
</div>
|
||||
<div class="packet-detail">
|
||||
SEQ=0, ACK=1
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
@@ -52,9 +84,16 @@
|
||||
<!-- ACK Packet -->
|
||||
<div class="packet-track">
|
||||
<transition name="slide-right">
|
||||
<div v-if="showAck" class="packet ack">
|
||||
<div class="packet-body">ACK</div>
|
||||
<div class="packet-detail">SEQ=1, ACK=1</div>
|
||||
<div
|
||||
v-if="showAck"
|
||||
class="packet ack"
|
||||
>
|
||||
<div class="packet-body">
|
||||
ACK
|
||||
</div>
|
||||
<div class="packet-detail">
|
||||
SEQ=1, ACK=1
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
@@ -66,9 +105,17 @@
|
||||
<span class="icon">🖥️</span>
|
||||
<span class="name">{{ t.server }}</span>
|
||||
</div>
|
||||
<div class="line"></div>
|
||||
<div class="state-marker" :class="{ active: step >= 2 }">SYN_RCVD</div>
|
||||
<div class="state-marker" :class="{ active: step >= 3 }">
|
||||
<div class="line" />
|
||||
<div
|
||||
class="state-marker"
|
||||
:class="{ active: step >= 2 }"
|
||||
>
|
||||
SYN_RCVD
|
||||
</div>
|
||||
<div
|
||||
class="state-marker"
|
||||
:class="{ active: step >= 3 }"
|
||||
>
|
||||
ESTABLISHED
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,40 +3,72 @@
|
||||
<div class="comparison-grid">
|
||||
<div class="protocol-card tcp">
|
||||
<div class="protocol-header">
|
||||
<div class="protocol-icon">🔒</div>
|
||||
<div class="protocol-title">TCP</div>
|
||||
<div class="protocol-subtitle">传输控制协议</div>
|
||||
<div class="protocol-icon">
|
||||
🔒
|
||||
</div>
|
||||
<div class="protocol-title">
|
||||
TCP
|
||||
</div>
|
||||
<div class="protocol-subtitle">
|
||||
传输控制协议
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="protocol-features">
|
||||
<div class="feature-item good">
|
||||
<div class="feature-icon">✓</div>
|
||||
<div class="feature-text">可靠传输</div>
|
||||
<div class="feature-icon">
|
||||
✓
|
||||
</div>
|
||||
<div class="feature-text">
|
||||
可靠传输
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature-item good">
|
||||
<div class="feature-icon">✓</div>
|
||||
<div class="feature-text">面向连接</div>
|
||||
<div class="feature-icon">
|
||||
✓
|
||||
</div>
|
||||
<div class="feature-text">
|
||||
面向连接
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature-item good">
|
||||
<div class="feature-icon">✓</div>
|
||||
<div class="feature-text">流量控制</div>
|
||||
<div class="feature-icon">
|
||||
✓
|
||||
</div>
|
||||
<div class="feature-text">
|
||||
流量控制
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature-item good">
|
||||
<div class="feature-icon">✓</div>
|
||||
<div class="feature-text">拥塞控制</div>
|
||||
<div class="feature-icon">
|
||||
✓
|
||||
</div>
|
||||
<div class="feature-text">
|
||||
拥塞控制
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature-item bad">
|
||||
<div class="feature-icon">✗</div>
|
||||
<div class="feature-text">速度较慢</div>
|
||||
<div class="feature-icon">
|
||||
✗
|
||||
</div>
|
||||
<div class="feature-text">
|
||||
速度较慢
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature-item bad">
|
||||
<div class="feature-icon">✗</div>
|
||||
<div class="feature-text">开销较大</div>
|
||||
<div class="feature-icon">
|
||||
✗
|
||||
</div>
|
||||
<div class="feature-text">
|
||||
开销较大
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="protocol-example">
|
||||
<div class="example-title">应用场景</div>
|
||||
<div class="example-title">
|
||||
应用场景
|
||||
</div>
|
||||
<div class="example-tags">
|
||||
<span class="tag">网页浏览</span>
|
||||
<span class="tag">文件传输</span>
|
||||
@@ -45,22 +77,48 @@
|
||||
</div>
|
||||
|
||||
<div class="handshake-demo">
|
||||
<div class="demo-title">三次握手</div>
|
||||
<div class="demo-title">
|
||||
三次握手
|
||||
</div>
|
||||
<div class="handshake-steps">
|
||||
<div class="step" :class="{ active: tcpStep >= 1 }">
|
||||
<div class="step-arrow">→</div>
|
||||
<div class="step-text">SYN</div>
|
||||
<div
|
||||
class="step"
|
||||
:class="{ active: tcpStep >= 1 }"
|
||||
>
|
||||
<div class="step-arrow">
|
||||
→
|
||||
</div>
|
||||
<div class="step-text">
|
||||
SYN
|
||||
</div>
|
||||
</div>
|
||||
<div class="step" :class="{ active: tcpStep >= 2 }">
|
||||
<div class="step-arrow">←</div>
|
||||
<div class="step-text">SYN-ACK</div>
|
||||
<div
|
||||
class="step"
|
||||
:class="{ active: tcpStep >= 2 }"
|
||||
>
|
||||
<div class="step-arrow">
|
||||
←
|
||||
</div>
|
||||
<div class="step-text">
|
||||
SYN-ACK
|
||||
</div>
|
||||
</div>
|
||||
<div class="step" :class="{ active: tcpStep >= 3 }">
|
||||
<div class="step-arrow">→</div>
|
||||
<div class="step-text">ACK</div>
|
||||
<div
|
||||
class="step"
|
||||
:class="{ active: tcpStep >= 3 }"
|
||||
>
|
||||
<div class="step-arrow">
|
||||
→
|
||||
</div>
|
||||
<div class="step-text">
|
||||
ACK
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="demo-btn" @click="startTcpHandshake">
|
||||
<button
|
||||
class="demo-btn"
|
||||
@click="startTcpHandshake"
|
||||
>
|
||||
{{ tcpStep === 0 ? '演示握手' : '重新演示' }}
|
||||
</button>
|
||||
</div>
|
||||
@@ -68,40 +126,72 @@
|
||||
|
||||
<div class="protocol-card udp">
|
||||
<div class="protocol-header">
|
||||
<div class="protocol-icon">⚡</div>
|
||||
<div class="protocol-title">UDP</div>
|
||||
<div class="protocol-subtitle">用户数据报协议</div>
|
||||
<div class="protocol-icon">
|
||||
⚡
|
||||
</div>
|
||||
<div class="protocol-title">
|
||||
UDP
|
||||
</div>
|
||||
<div class="protocol-subtitle">
|
||||
用户数据报协议
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="protocol-features">
|
||||
<div class="feature-item good">
|
||||
<div class="feature-icon">✓</div>
|
||||
<div class="feature-text">快速传输</div>
|
||||
<div class="feature-icon">
|
||||
✓
|
||||
</div>
|
||||
<div class="feature-text">
|
||||
快速传输
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature-item good">
|
||||
<div class="feature-icon">✓</div>
|
||||
<div class="feature-text">开销小</div>
|
||||
<div class="feature-icon">
|
||||
✓
|
||||
</div>
|
||||
<div class="feature-text">
|
||||
开销小
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature-item good">
|
||||
<div class="feature-icon">✓</div>
|
||||
<div class="feature-text">无连接</div>
|
||||
<div class="feature-icon">
|
||||
✓
|
||||
</div>
|
||||
<div class="feature-text">
|
||||
无连接
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature-item good">
|
||||
<div class="feature-icon">✓</div>
|
||||
<div class="feature-text">支持多播</div>
|
||||
<div class="feature-icon">
|
||||
✓
|
||||
</div>
|
||||
<div class="feature-text">
|
||||
支持多播
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature-item bad">
|
||||
<div class="feature-icon">✗</div>
|
||||
<div class="feature-text">不可靠</div>
|
||||
<div class="feature-icon">
|
||||
✗
|
||||
</div>
|
||||
<div class="feature-text">
|
||||
不可靠
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature-item bad">
|
||||
<div class="feature-icon">✗</div>
|
||||
<div class="feature-text">可能丢包</div>
|
||||
<div class="feature-icon">
|
||||
✗
|
||||
</div>
|
||||
<div class="feature-text">
|
||||
可能丢包
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="protocol-example">
|
||||
<div class="example-title">应用场景</div>
|
||||
<div class="example-title">
|
||||
应用场景
|
||||
</div>
|
||||
<div class="example-tags">
|
||||
<span class="tag">视频直播</span>
|
||||
<span class="tag">在线游戏</span>
|
||||
@@ -110,14 +200,23 @@
|
||||
</div>
|
||||
|
||||
<div class="handshake-demo">
|
||||
<div class="demo-title">直接发送</div>
|
||||
<div class="demo-title">
|
||||
直接发送
|
||||
</div>
|
||||
<div class="handshake-steps">
|
||||
<div class="step direct">
|
||||
<div class="step-arrow">→</div>
|
||||
<div class="step-text">直接发送数据</div>
|
||||
<div class="step-arrow">
|
||||
→
|
||||
</div>
|
||||
<div class="step-text">
|
||||
直接发送数据
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="demo-btn" @click="sendUdpData">
|
||||
<button
|
||||
class="demo-btn"
|
||||
@click="sendUdpData"
|
||||
>
|
||||
{{ udpSent ? '再发一次' : '发送数据' }}
|
||||
</button>
|
||||
</div>
|
||||
@@ -169,37 +268,55 @@
|
||||
</div>
|
||||
|
||||
<div class="real-world-example">
|
||||
<div class="example-title">🎬 实际应用示例</div>
|
||||
<div class="example-title">
|
||||
🎬 实际应用示例
|
||||
</div>
|
||||
<div class="scenario-grid">
|
||||
<div class="scenario">
|
||||
<div class="scenario-icon">📺</div>
|
||||
<div class="scenario-name">视频直播</div>
|
||||
<div class="scenario-icon">
|
||||
📺
|
||||
</div>
|
||||
<div class="scenario-name">
|
||||
视频直播
|
||||
</div>
|
||||
<div class="scenario-desc">
|
||||
使用 <strong>UDP</strong>,因为: <br />• 丢几帧没关系,关键是实时
|
||||
<br />• 重传会造成延迟和卡顿
|
||||
使用 <strong>UDP</strong>,因为: <br>• 丢几帧没关系,关键是实时
|
||||
<br>• 重传会造成延迟和卡顿
|
||||
</div>
|
||||
</div>
|
||||
<div class="scenario">
|
||||
<div class="scenario-icon">🌐</div>
|
||||
<div class="scenario-name">网页浏览</div>
|
||||
<div class="scenario-icon">
|
||||
🌐
|
||||
</div>
|
||||
<div class="scenario-name">
|
||||
网页浏览
|
||||
</div>
|
||||
<div class="scenario-desc">
|
||||
使用 <strong>TCP</strong>,因为: <br />• 内容必须完整准确 <br />•
|
||||
使用 <strong>TCP</strong>,因为: <br>• 内容必须完整准确 <br>•
|
||||
丢失任何数据都不可接受
|
||||
</div>
|
||||
</div>
|
||||
<div class="scenario">
|
||||
<div class="scenario-icon">🎮</div>
|
||||
<div class="scenario-name">在线游戏</div>
|
||||
<div class="scenario-icon">
|
||||
🎮
|
||||
</div>
|
||||
<div class="scenario-name">
|
||||
在线游戏
|
||||
</div>
|
||||
<div class="scenario-desc">
|
||||
使用 <strong>UDP</strong>,因为: <br />• 响应速度比准确更重要
|
||||
<br />• 实时同步玩家位置
|
||||
使用 <strong>UDP</strong>,因为: <br>• 响应速度比准确更重要
|
||||
<br>• 实时同步玩家位置
|
||||
</div>
|
||||
</div>
|
||||
<div class="scenario">
|
||||
<div class="scenario-icon">📧</div>
|
||||
<div class="scenario-name">邮件发送</div>
|
||||
<div class="scenario-icon">
|
||||
📧
|
||||
</div>
|
||||
<div class="scenario-name">
|
||||
邮件发送
|
||||
</div>
|
||||
<div class="scenario-desc">
|
||||
使用 <strong>TCP</strong>,因为: <br />• 邮件内容不能丢失 <br />•
|
||||
使用 <strong>TCP</strong>,因为: <br>• 邮件内容不能丢失 <br>•
|
||||
可靠性是第一要务
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -9,53 +9,50 @@
|
||||
<div class="omnibox">
|
||||
<span class="lock-icon">🔒</span>
|
||||
<!-- Segmented URL Display -->
|
||||
<div class="segmented-url" v-if="parsedUrl">
|
||||
<div
|
||||
v-if="parsedUrl"
|
||||
class="segmented-url"
|
||||
>
|
||||
<span
|
||||
class="url-part protocol"
|
||||
:class="{ active: highlightedPart === 'protocol' }"
|
||||
@mouseover="highlightedPart = 'protocol'"
|
||||
@mouseleave="highlightedPart = null"
|
||||
>{{ parts.protocol }}:</span
|
||||
>
|
||||
>{{ parts.protocol }}:</span>
|
||||
<span class="divider">//</span>
|
||||
<span
|
||||
class="url-part host"
|
||||
:class="{ active: highlightedPart === 'host' }"
|
||||
@mouseover="highlightedPart = 'host'"
|
||||
@mouseleave="highlightedPart = null"
|
||||
>{{ parts.host }}</span
|
||||
>
|
||||
>{{ parts.host }}</span>
|
||||
<span
|
||||
v-if="parts.port"
|
||||
class="url-part port"
|
||||
:class="{ active: highlightedPart === 'port' }"
|
||||
@mouseover="highlightedPart = 'port'"
|
||||
@mouseleave="highlightedPart = null"
|
||||
>:{{ parts.port }}</span
|
||||
>
|
||||
>:{{ parts.port }}</span>
|
||||
<span
|
||||
class="url-part pathname"
|
||||
:class="{ active: highlightedPart === 'pathname' }"
|
||||
@mouseover="highlightedPart = 'pathname'"
|
||||
@mouseleave="highlightedPart = null"
|
||||
>{{ parts.pathname }}</span
|
||||
>
|
||||
>{{ parts.pathname }}</span>
|
||||
<span
|
||||
v-if="parts.search"
|
||||
class="url-part search"
|
||||
:class="{ active: highlightedPart === 'search' }"
|
||||
@mouseover="highlightedPart = 'search'"
|
||||
@mouseleave="highlightedPart = null"
|
||||
>{{ parts.search }}</span
|
||||
>
|
||||
>{{ parts.search }}</span>
|
||||
<span
|
||||
v-if="parts.hash"
|
||||
class="url-part hash"
|
||||
:class="{ active: highlightedPart === 'hash' }"
|
||||
@mouseover="highlightedPart = 'hash'"
|
||||
@mouseleave="highlightedPart = null"
|
||||
>{{ parts.hash }}</span
|
||||
>
|
||||
>{{ parts.hash }}</span>
|
||||
</div>
|
||||
<input
|
||||
v-else
|
||||
@@ -63,12 +60,15 @@
|
||||
type="text"
|
||||
class="url-input"
|
||||
placeholder="https://example.com"
|
||||
/>
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="visualization-area">
|
||||
<div v-if="parsedUrl" class="url-breakdown">
|
||||
<div
|
||||
v-if="parsedUrl"
|
||||
class="url-breakdown"
|
||||
>
|
||||
<div
|
||||
v-for="(part, key) in parts"
|
||||
:key="key"
|
||||
@@ -81,11 +81,20 @@
|
||||
<span class="segment-icon">{{ icons[key] }}</span>
|
||||
<span class="segment-label">{{ labels[key] }}</span>
|
||||
</div>
|
||||
<div class="segment-value">{{ part || '-' }}</div>
|
||||
<div class="segment-desc">{{ descriptions[key] }}</div>
|
||||
<div class="segment-value">
|
||||
{{ part || '-' }}
|
||||
</div>
|
||||
<div class="segment-desc">
|
||||
{{ descriptions[key] }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="error-state">Invalid URL format / 无效的 URL 格式</div>
|
||||
<div
|
||||
v-else
|
||||
class="error-state"
|
||||
>
|
||||
Invalid URL format / 无效的 URL 格式
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<div
|
||||
class="line-fill"
|
||||
:style="{ width: (currentStage / (stages.length - 1)) * 100 + '%' }"
|
||||
></div>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -31,13 +31,27 @@
|
||||
</div>
|
||||
|
||||
<div class="component-wrapper">
|
||||
<transition name="fade" mode="out-in">
|
||||
<component :is="stages[currentStage].component" :key="currentStage" />
|
||||
<transition
|
||||
name="fade"
|
||||
mode="out-in"
|
||||
>
|
||||
<component
|
||||
:is="stages[currentStage].component"
|
||||
:key="currentStage"
|
||||
/>
|
||||
</transition>
|
||||
</div>
|
||||
|
||||
<div class="action-footer" v-if="currentStage < stages.length - 1">
|
||||
<button class="next-btn" @click="nextStage">下一步 →</button>
|
||||
<div
|
||||
v-if="currentStage < stages.length - 1"
|
||||
class="action-footer"
|
||||
>
|
||||
<button
|
||||
class="next-btn"
|
||||
@click="nextStage"
|
||||
>
|
||||
下一步 →
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
<template>
|
||||
<div class="vr-demo">
|
||||
<div class="header">
|
||||
<div class="title">Vue vs React:它们哪里像?哪里不一样?</div>
|
||||
<div class="title">
|
||||
Vue vs React:它们哪里像?哪里不一样?
|
||||
</div>
|
||||
<div class="subtitle">
|
||||
选一个标签页,然后点“+1”,看看背后发生了什么(示意)。
|
||||
</div>
|
||||
@@ -25,36 +27,56 @@
|
||||
|
||||
<div class="grid">
|
||||
<div class="panel">
|
||||
<div class="panel-title">Vue</div>
|
||||
<div class="panel-title">
|
||||
Vue
|
||||
</div>
|
||||
<div class="preview">
|
||||
<div class="row">
|
||||
count: <strong>{{ count }}</strong>
|
||||
</div>
|
||||
<button class="btn vue" @click="inc('vue')">+1</button>
|
||||
<button
|
||||
class="btn vue"
|
||||
@click="inc('vue')"
|
||||
>
|
||||
+1
|
||||
</button>
|
||||
</div>
|
||||
<div class="code">
|
||||
<div class="code-title">典型写法(示意)</div>
|
||||
<div class="code-title">
|
||||
典型写法(示意)
|
||||
</div>
|
||||
<pre><code class="language-vue">{{ vueCode }}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-title">React</div>
|
||||
<div class="panel-title">
|
||||
React
|
||||
</div>
|
||||
<div class="preview">
|
||||
<div class="row">
|
||||
count: <strong>{{ count }}</strong>
|
||||
</div>
|
||||
<button class="btn react" @click="inc('react')">+1</button>
|
||||
<button
|
||||
class="btn react"
|
||||
@click="inc('react')"
|
||||
>
|
||||
+1
|
||||
</button>
|
||||
</div>
|
||||
<div class="code">
|
||||
<div class="code-title">典型写法(示意)</div>
|
||||
<div class="code-title">
|
||||
典型写法(示意)
|
||||
</div>
|
||||
<pre><code class="language-jsx">{{ reactCode }}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="what">
|
||||
<div class="what-title">点击 “+1” 时发生了什么?</div>
|
||||
<div class="what-title">
|
||||
点击 “+1” 时发生了什么?
|
||||
</div>
|
||||
<div class="steps">
|
||||
<div
|
||||
v-for="(s, idx) in steps"
|
||||
|
||||
@@ -19,14 +19,29 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="preview" :class="current">
|
||||
<h1 class="hero" :class="{ selected: selectedPart === 'h1' }" @click="selectedPart = 'h1'">
|
||||
<div
|
||||
class="preview"
|
||||
:class="current"
|
||||
>
|
||||
<h1
|
||||
class="hero"
|
||||
:class="{ selected: selectedPart === 'h1' }"
|
||||
@click="selectedPart = 'h1'"
|
||||
>
|
||||
<span class="badge">①</span>欢迎来到我的网站
|
||||
</h1>
|
||||
<p class="desc" :class="{ selected: selectedPart === 'p' }" @click="selectedPart = 'p'">
|
||||
<p
|
||||
class="desc"
|
||||
:class="{ selected: selectedPart === 'p' }"
|
||||
@click="selectedPart = 'p'"
|
||||
>
|
||||
<span class="badge">②</span>这是一段描述文字
|
||||
</p>
|
||||
<button class="cta" :class="{ selected: selectedPart === 'btn' }" @click="handleBtnClick">
|
||||
<button
|
||||
class="cta"
|
||||
:class="{ selected: selectedPart === 'btn' }"
|
||||
@click="handleBtnClick"
|
||||
>
|
||||
<span class="badge">③</span>点我试试 ({{ clicks }})
|
||||
</button>
|
||||
</div>
|
||||
@@ -34,18 +49,31 @@
|
||||
|
||||
<div class="right-panel">
|
||||
<div class="code-section">
|
||||
<div class="code-label">{{ codeTitle }}</div>
|
||||
<div class="code-label">
|
||||
{{ codeTitle }}
|
||||
</div>
|
||||
<div class="code-block">
|
||||
<div v-for="(line, i) in codeLines" :key="i" :class="['line', { hl: line.key === selectedPart }]">
|
||||
<div
|
||||
v-for="(line, i) in codeLines"
|
||||
:key="i"
|
||||
:class="['line', { hl: line.key === selectedPart }]"
|
||||
>
|
||||
{{ line.text }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="explain-section">
|
||||
<div class="explain-label">执行过程</div>
|
||||
<div class="explain-label">
|
||||
执行过程
|
||||
</div>
|
||||
<ol class="steps">
|
||||
<li v-for="s in steps" :key="s">{{ s }}</li>
|
||||
<li
|
||||
v-for="s in steps"
|
||||
:key="s"
|
||||
>
|
||||
{{ s }}
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user