feat: update docs and components, fix DLQ demo bug
This commit is contained in:
@@ -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:
|
||||
'这已经不是租房了,是包下了一整层楼。通常需要多台机器配合,专人维护。'
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user