feat: update docs and components, fix DLQ demo bug

This commit is contained in:
sanbuphy
2026-01-18 12:21:49 +08:00
parent 26ed39e1eb
commit e41063a1cd
159 changed files with 54236 additions and 2525 deletions
@@ -26,7 +26,9 @@
<div class="control">
<label>快递柜里有吗(命中率)</label>
<input type="range" min="0" max="100" v-model.number="hit" />
<div class="hint">当前概率{{ hit }}% ({{ hit > 80 ? '大部分都有' : '经常要跑远路' }})</div>
<div class="hint">
当前概率{{ hit }}% ({{ hit > 80 ? '大部分都有' : '经常要跑远路' }})
</div>
</div>
</div>
@@ -10,7 +10,10 @@
<div class="subtitle">就像一台全自动炒菜机</div>
</div>
<div class="actions">
<label class="fail-toggle"><input type="checkbox" v-model="failTest" /> 混入一颗烂菜 (模拟报错)</label>
<label class="fail-toggle"
><input type="checkbox" v-model="failTest" /> 混入一颗烂菜
(模拟报错)</label
>
<button :disabled="running" @click="run" class="run-btn">
{{ running ? '机器运转中...' : '开始做菜 (触发构建)' }}
</button>
@@ -48,12 +51,12 @@ const steps = ref([
desc: 'npm install',
status: 'idle'
},
{
id: 'test',
name: '自动测试 (Test)',
analogy: '🔍 食品安检',
desc: 'npm test',
status: 'idle'
{
id: 'test',
name: '自动测试 (Test)',
analogy: '🔍 食品安检',
desc: 'npm test',
status: 'idle'
},
{
id: 'build',
@@ -105,10 +108,10 @@ const run = async () => {
ms: 800,
log: '> 正在检查食材新鲜度...\n> 单元测试运行中...'
},
{
id: 'build',
ms: 1200,
log: '> 开始烹饪...\n> 正在压缩混淆代码...\n> 产出 dist/ 目录 (一盘好菜)'
{
id: 'build',
ms: 1200,
log: '> 开始烹饪...\n> 正在压缩混淆代码...\n> 产出 dist/ 目录 (一盘好菜)'
},
{
id: 'deploy',
@@ -125,7 +128,8 @@ const run = async () => {
if (item.id === 'test' && failTest.value) {
step.status = 'fail'
log.value = '❌ 警告:发现一颗烂白菜!(测试失败)\n❌ 立即停机,防止端给顾客。'
log.value =
'❌ 警告:发现一颗烂白菜!(测试失败)\n❌ 立即停机,防止端给顾客。'
steps.value
.filter((s) => s.id !== 'test')
.forEach((s) => (s.status = 'idle'))
@@ -68,25 +68,73 @@ import { computed, ref } from 'vue'
const modes = [
{ id: 'static', label: '看海报 (静态)', icon: '🖼️' },
{ id: 'spa', label: '玩 App (SPA)', icon: '📱' },
{ id: 'ssr', label: '刷动态 (SSR)', icon: '🔄' },
{ id: 'ssr', label: '刷动态 (SSR)', icon: '🔄' }
]
const currentMode = ref('spa')
const currentModeLabel = computed(() =>
modes.find(m => m.id === currentMode.value)?.label
const currentModeLabel = computed(
() => modes.find((m) => m.id === currentMode.value)?.label
)
// 角色:User(寄件人), DNS(查号台), CDN(快递柜), WAF(保安), LB(大堂经理), Server(办事员), DB(档案室)
const commonNodes = {
user: { role: '寄件人', name: 'User', icon: '🧑', color: '#64748b', desc: '发出请求' },
dns: { role: '查号台', name: 'DNS', icon: '📒', color: '#0ea5e9', desc: '查询 IP 地址' },
cdn: { role: '快递柜', name: 'CDN', icon: '📦', color: '#22c55e', desc: '就近取货' },
waf: { role: '保安', name: 'WAF', icon: '🛡️', color: '#ef4444', desc: '拦截黑客' },
lb: { role: '大堂经理', name: 'LB', icon: '💁', color: '#f59e0b', desc: '分配窗口' },
server: { role: '办事员', name: 'Server', icon: '👨‍💼', color: '#8b5cf6', desc: '处理业务' },
db: { role: '档案室', name: 'Database', icon: '🗄️', color: '#d946ef', desc: '存取数据' },
obj: { role: '仓库', name: 'OSS', icon: '🏭', color: '#f97316', desc: '拿静态文件' }
user: {
role: '寄件人',
name: 'User',
icon: '🧑',
color: '#64748b',
desc: '发出请求'
},
dns: {
role: '查号台',
name: 'DNS',
icon: '📒',
color: '#0ea5e9',
desc: '查询 IP 地址'
},
cdn: {
role: '快递柜',
name: 'CDN',
icon: '📦',
color: '#22c55e',
desc: '就近取货'
},
waf: {
role: '保安',
name: 'WAF',
icon: '🛡️',
color: '#ef4444',
desc: '拦截黑客'
},
lb: {
role: '大堂经理',
name: 'LB',
icon: '💁',
color: '#f59e0b',
desc: '分配窗口'
},
server: {
role: '办事员',
name: 'Server',
icon: '👨‍💼',
color: '#8b5cf6',
desc: '处理业务'
},
db: {
role: '档案室',
name: 'Database',
icon: '🗄️',
color: '#d946ef',
desc: '存取数据'
},
obj: {
role: '仓库',
name: 'OSS',
icon: '🏭',
color: '#f97316',
desc: '拿静态文件'
}
}
const flowMap = {
@@ -116,10 +164,14 @@ const nodes = computed(() => flowMap[currentMode.value])
const bottleneck = computed(() => {
switch (currentMode.value) {
case 'static': return '几乎没有瓶颈,起飞!'
case 'spa': return 'API 接口响应速度'
case 'ssr': return '办事员 (Server) 拼装页面的速度'
default: return ''
case 'static':
return '几乎没有瓶颈,起飞!'
case 'spa':
return 'API 接口响应速度'
case 'ssr':
return '办事员 (Server) 拼装页面的速度'
default:
return ''
}
})
@@ -46,7 +46,7 @@
<span>记忆时间 (TTL)</span>
<code>{{ ttlSuggestion }}</code>
</div>
<div class="human-speak">
<span class="emoji">💡</span>
<div class="text">
@@ -88,13 +88,14 @@ const recordTypes = [
}
]
const currentRecord = computed(() => recordTypes.find(r => r.type === recordType.value))
const currentRecord = computed(() =>
recordTypes.find((r) => r.type === recordType.value)
)
const hostLabel = computed(() => (recordType.value === 'CNAME' ? 'www' : '@'))
const recordValue = computed(() => currentRecord.value?.value || '')
const ttlSuggestion = computed(() => currentRecord.value?.ttl || '600s')
const humanExplanation = computed(() => currentRecord.value?.explanation || '')
</script>
<style scoped>
@@ -10,16 +10,16 @@
</div>
<div class="options">
<div
class="option"
<div
class="option"
:class="{ active: mode === 'static' }"
@click="mode = 'static'"
>
<span class="icon">📄</span>
<span>静态网站</span>
</div>
<div
class="option"
<div
class="option"
:class="{ active: mode === 'proxy' }"
@click="mode = 'proxy'"
>
@@ -31,7 +31,9 @@
<div class="code-box">
<div class="code-header">
<span>/etc/nginx/sites-available/default</span>
<button class="copy-btn" @click="copy">{{ copied ? '已复制' : '复制' }}</button>
<button class="copy-btn" @click="copy">
{{ copied ? '已复制' : '复制' }}
</button>
</div>
<pre><code>{{ snippet }}</code></pre>
</div>
@@ -42,7 +44,9 @@
<div>
<strong>开启 HTTPS 神器</strong>
<div class="cmd">sudo certbot --nginx</div>
<div class="desc">运行这行命令它会自动修改上面的配置帮你加上 SSL 证书</div>
<div class="desc">
运行这行命令它会自动修改上面的配置帮你加上 SSL 证书
</div>
</div>
</div>
</div>
@@ -89,7 +93,7 @@ const snippet = computed(() => {
function copy() {
navigator.clipboard.writeText(snippet.value)
copied.value = true
setTimeout(() => copied.value = false, 2000)
setTimeout(() => (copied.value = false), 2000)
}
</script>
@@ -161,7 +165,7 @@ function copy() {
.copy-btn {
color: #fff;
background: rgba(255,255,255,0.1);
background: rgba(255, 255, 255, 0.1);
padding: 2px 8px;
border-radius: 4px;
font-size: 11px;
@@ -57,7 +57,15 @@
>
{{ 100 - riskScore }}
</div>
<div class="note">{{ riskScore > 70 ? '极其危险!' : (riskScore > 40 ? '勉强及格' : '非常稳!') }}</div>
<div class="note">
{{
riskScore > 70
? '极其危险!'
: riskScore > 40
? '勉强及格'
: '非常稳!'
}}
</div>
</div>
<div class="card">
<div class="label">最坏情况 (丢数据)</div>
@@ -86,7 +86,8 @@ const analogy = computed(() => {
return '有钱任性。在隔壁新开一家一模一样的店。装修好了,直接把大门指路牌改到新店。'
case 'canary':
return '先让 VIP 客户去新包间体验一下。如果 VIP 没投诉,再把所有客人都请进去。'
default: return ''
default:
return ''
}
})
@@ -140,7 +141,7 @@ const risk = computed(() => {
border-color: var(--vp-c-brand);
color: var(--vp-c-brand);
background: var(--vp-c-bg-soft);
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
}
.stats {
@@ -13,7 +13,13 @@
<div class="control">
<label>你的业务规模</label>
<div class="range-box">
<input type="range" min="0" max="3" step="1" v-model.number="scaleIndex" />
<input
type="range"
min="0"
max="3"
step="1"
v-model.number="scaleIndex"
/>
<div class="scale-labels">
<span>个人博客</span>
<span>初创官网</span>
@@ -72,7 +78,8 @@ const levels = [
icon: '🚗',
bandwidth: '3~5 Mbps',
cost: '¥300 / 年',
analogy: '就像租了个两居室。正经跑个公司官网、小程序后端没问题。大多数人的首选。'
analogy:
'就像租了个两居室。正经跑个公司官网、小程序后端没问题。大多数人的首选。'
},
{
title: '专业级 (Pro)',
@@ -80,7 +87,8 @@ const levels = [
icon: '🏎️',
bandwidth: '5~10 Mbps',
cost: '¥1000+ / 年',
analogy: '就像租了个大平层办公室。能抗住几千人同时在线,跑复杂的计算任务也不虚。'
analogy:
'就像租了个大平层办公室。能抗住几千人同时在线,跑复杂的计算任务也不虚。'
},
{
title: '企业级 (Enterprise)',
@@ -88,7 +96,8 @@ const levels = [
icon: '✈️',
bandwidth: '按量付费',
cost: '¥5000+ / 年',
analogy: '这已经不是租房了,是包下了一整层楼。通常需要多台机器配合,专人维护。'
analogy:
'这已经不是租房了,是包下了一整层楼。通常需要多台机器配合,专人维护。'
}
]