Files
test-repo/docs/.vitepress/theme/components/appendix/url-to-browser/HttpExchangeDemo.vue
T
sanbuphy 084ebed417 docs: update Chinese documentation and add Vue components
- Update AI capability dictionary by removing redundant mention of Baidu's model
- Add new Vue components for context engineering visualization (IntroProblemReasonSolution, MemoryPalaceDemo, MemoryPalaceActionDemo, KVCacheDemo, LostInMiddleDemo)
- Register new components in theme index.js
- Enhance audio introduction with new interactive demos (AudioQuickStartDemo, MelSpectrogramDemo, TTSPipelineDemo, VoiceCloningDemo, ASRvsTTSDemo, AudioTokenizationDemo, EmotionControlDemo)
- Improve existing context engineering demos with Chinese localization and better tokenization
- Fix Japanese documentation layout by properly closing NavGrid components
2026-02-03 19:41:14 +08:00

534 lines
12 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!--
HttpExchangeDemo.vue
HTTP请求响应演示 - 快递员送达对话类比
用途
"快递员和收件人对话"的生活化比喻让用户理解HTTP请求和响应的过程
把枯燥的HTTP协议变成直观的对话场景
-->
<template>
<div class="delivery-dialog-demo">
<!-- 标题 -->
<div class="dialog-header">
<span class="dialog-icon">[送达]</span>
<span class="dialog-title">送达对话请求与响应</span>
</div>
<!-- 场景选择 -->
<div class="scenario-selector">
<div class="selector-label">选择送达场景</div>
<div class="scenario-buttons">
<button
v-for="scene in scenarios"
:key="scene.id"
@click="selectScenario(scene)"
class="scenario-btn"
:class="{ active: currentScenario?.id === scene.id }"
:disabled="isDelivering"
>
<span class="btn-text">{{ scene.name }}</span>
</button>
</div>
</div>
<!-- 对话场景 -->
<div class="dialog-scene" v-if="currentScenario">
<div class="scene-background">
<!-- 快递员请求方 -->
<div class="character courier">
<div class="char-avatar"></div>
<div class="char-name">快递员浏览器</div>
</div>
<!-- 对话区域 -->
<div class="conversation-area">
<!-- 请求消息 -->
<div class="message request" :class="{ sent: step >= 1 }">
<div class="message-bubble">
<div class="bubble-header">
<span class="method-badge" :class="currentScenario.method.toLowerCase()">
{{ currentScenario.method }}
</span>
<span class="path-text">{{ currentScenario.path }}</span>
</div>
<div class="bubble-body">{{ currentScenario.requestText }}</div>
</div>
<div class="message-meta">请求</div>
</div>
<!-- 传输动画 -->
<div class="transit-animation" v-if="step === 2">
<div class="transit-line"></div>
<div class="transit-package"></div>
</div>
<!-- 响应消息 -->
<div class="message response" :class="{ sent: step >= 3 }">
<div class="message-meta">响应</div>
<div class="message-bubble" :class="currentScenario.statusType">
<div class="bubble-header">
<span class="status-badge" :class="currentScenario.statusType">
{{ currentScenario.status }}
</span>
<span class="status-text">{{ currentScenario.statusText }}</span>
</div>
<div class="bubble-body">{{ currentScenario.responseText }}</div>
</div>
</div>
</div>
<!-- 收件人响应方 -->
<div class="character recipient">
<div class="char-avatar"></div>
<div class="char-name">收件人服务器</div>
</div>
</div>
</div>
<!-- 控制按钮 -->
<div class="dialog-controls">
<button
class="control-btn primary"
@click="nextStep"
:disabled="isDelivering || step >= 3"
>
{{ step === 0 ? '[开始]' : step === 3 ? '对话完成' : '[下一步]' }}
</button>
<button class="control-btn" @click="reset" v-if="step > 0">
[重置]
</button>
</div>
<!-- 状态码说明 -->
<div class="status-legend">
<div class="legend-title">[对照] HTTP状态码速查</div>
<div class="legend-grid">
<div class="legend-item success">
<span class="status-dot"></span>
<span class="status-code">2xx</span>
<span class="status-meaning">成功送达</span>
</div>
<div class="legend-item redirect">
<span class="status-dot"></span>
<span class="status-code">3xx</span>
<span class="status-meaning">地址变更</span>
</div>
<div class="legend-item client-error">
<span class="status-dot"></span>
<span class="status-code">4xx</span>
<span class="status-meaning">请求有误</span>
</div>
<div class="legend-item server-error">
<span class="status-dot"></span>
<span class="status-code">5xx</span>
<span class="status-meaning">服务器问题</span>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const scenarios = [
{
id: 'success',
name: '正常送达',
method: 'GET',
path: '/index.html',
requestText: '您好,这是您的包裹,请签收!',
status: '200',
statusText: 'OK',
statusType: 'success',
responseText: '好的,收到了,谢谢!'
},
{
id: 'notfound',
name: '地址错误',
method: 'GET',
path: '/nopage',
requestText: '您好,送包裹到这个地方。',
status: '404',
statusText: 'Not Found',
statusType: 'client-error',
responseText: '这里没有这个人,您送错地方了。'
},
{
id: 'redirect',
name: '地址变更',
method: 'GET',
path: '/old-address',
requestText: '您好,送包裹到这个地址。',
status: '301',
statusText: 'Moved',
statusType: 'redirect',
responseText: '这里搬走了,请送到新地址。'
},
{
id: 'error',
name: '家中故障',
method: 'POST',
path: '/api/order',
requestText: '您好,我来送您订购的商品。',
status: '500',
statusText: 'Error',
statusType: 'server-error',
responseText: '抱歉,我们家系统出问题了,暂时无法接收。'
}
]
const currentScenario = ref(scenarios[0])
const step = ref(0)
const isDelivering = ref(false)
const selectScenario = (scenario) => {
currentScenario.value = scenario
reset()
}
const nextStep = () => {
if (step.value < 3) {
isDelivering.value = true
step.value++
if (step.value === 2) {
setTimeout(() => {
step.value++
isDelivering.value = false
}, 1000)
} else {
isDelivering.value = false
}
}
}
const reset = () => {
step.value = 0
isDelivering.value = false
}
</script>
<style scoped>
.delivery-dialog-demo {
background: linear-gradient(135deg, var(--vp-c-bg-soft) 0%, var(--vp-c-bg) 100%);
border: 2px solid var(--vp-c-divider);
border-radius: 16px;
padding: 24px;
margin: 20px 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
/* 头部 */
.dialog-header {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 20px;
padding-bottom: 16px;
border-bottom: 1px solid var(--vp-c-divider);
}
.dialog-icon {
font-size: 14px;
color: var(--vp-c-brand);
font-weight: 600;
}
.dialog-title {
font-size: 17px;
font-weight: 700;
color: var(--vp-c-text-1);
}
/* 场景选择 */
.scenario-selector {
margin-bottom: 20px;
}
.selector-label {
font-size: 13px;
color: var(--vp-c-text-3);
margin-bottom: 10px;
}
.scenario-buttons {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.scenario-btn {
display: flex;
align-items: center;
gap: 8px;
padding: 10px 16px;
background: var(--vp-c-bg);
border: 2px solid var(--vp-c-divider);
border-radius: 20px;
cursor: pointer;
transition: all 0.3s;
}
.scenario-btn:hover:not(:disabled) {
border-color: var(--vp-c-brand);
transform: translateY(-2px);
}
.scenario-btn.active {
background: var(--vp-c-brand);
border-color: var(--vp-c-brand);
color: white;
}
.scenario-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.btn-text { font-size: 13px; }
/* 对话场景 */
.dialog-scene {
background: var(--vp-c-bg);
border-radius: 12px;
padding: 20px;
margin-bottom: 20px;
}
.scene-background {
display: flex;
align-items: center;
gap: 20px;
}
/* 角色 */
.character {
text-align: center;
flex-shrink: 0;
}
.char-avatar {
width: 60px;
height: 60px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
font-weight: 600;
color: white;
margin-bottom: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
.courier .char-avatar {
background: linear-gradient(135deg, #409eff, #67c23a);
}
.recipient .char-avatar {
background: linear-gradient(135deg, #e6a23c, #f56c6c);
}
.char-name {
font-size: 12px;
color: var(--vp-c-text-2);
font-weight: 500;
}
/* 对话区域 */
.conversation-area {
flex: 1;
display: flex;
flex-direction: column;
gap: 16px;
}
.message {
display: flex;
align-items: center;
gap: 12px;
opacity: 0.3;
transform: translateY(10px);
transition: all 0.4s;
}
.message.sent {
opacity: 1;
transform: translateY(0);
}
.message.request {
justify-content: flex-end;
}
.message.response {
justify-content: flex-start;
}
.message-bubble {
max-width: 280px;
padding: 14px;
border-radius: 12px;
background: var(--vp-c-bg-soft);
border: 2px solid var(--vp-c-divider);
}
.message.request .message-bubble {
background: #409eff;
border-color: #409eff;
color: white;
}
.message.response .message-bubble.success {
background: #67c23a;
border-color: #67c23a;
color: white;
}
.message.response .message-bubble.redirect {
background: #e6a23c;
border-color: #e6a23c;
color: white;
}
.message.response .message-bubble.client-error {
background: #f56c6c;
border-color: #f56c6c;
color: white;
}
.message.response .message-bubble.server-error {
background: #909399;
border-color: #909399;
color: white;
}
.bubble-header {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 8px;
padding-bottom: 8px;
border-bottom: 1px solid rgba(255,255,255,0.2);
}
.method-badge, .status-badge {
padding: 2px 8px;
border-radius: 4px;
font-size: 11px;
font-weight: 700;
font-family: monospace;
background: rgba(255,255,255,0.2);
}
.path-text, .status-text {
font-size: 12px;
opacity: 0.9;
}
.bubble-body {
font-size: 13px;
line-height: 1.5;
}
.message-meta {
font-size: 11px;
color: var(--vp-c-text-3);
font-weight: 500;
}
/* 传输动画 */
.transit-animation {
display: flex;
align-items: center;
justify-content: center;
position: relative;
height: 40px;
}
.transit-line {
width: 100%;
height: 2px;
background: var(--vp-c-divider);
}
.transit-package {
position: absolute;
font-size: 20px;
font-weight: 600;
color: var(--vp-c-brand);
animation: deliver 1s ease-in-out;
}
@keyframes deliver {
0% { transform: translateX(-100px); }
50% { transform: translateX(0) scale(1.2); }
100% { transform: translateX(100px); }
}
/* 控制按钮 */
.dialog-controls {
display: flex;
justify-content: center;
gap: 12px;
margin-bottom: 20px;
}
.control-btn {
padding: 12px 24px;
background: var(--vp-c-bg);
border: 2px solid var(--vp-c-divider);
border-radius: 24px;
cursor: pointer;
font-size: 14px;
color: var(--vp-c-text-2);
transition: all 0.3s;
}
.control-btn:hover:not(:disabled) {
border-color: var(--vp-c-brand);
color: var(--vp-c-brand);
}
.control-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.control-btn.primary {
background: var(--vp-c-brand);
border-color: var(--vp-c-brand);
color: white;
}
.control-btn.primary:hover:not(:disabled) {
background: #66b1ff;
}
/* 状态码说明 */
.status-legend {
padding: 16px;
background: var(--vp-c-bg);
border-radius: 12px;
}
.legend-title {
font-size: 14px;
font-weight: 600;
color: var(--vp-c-text-1);
margin-bottom: 12px;
}
.legend-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 10px;
}
.legend-item {
display: flex;
align-items: center;
gap: 8px;
padding: 10px;
background: var(--vp-c-bg-soft);
border-radius: 8px;
}
.status-dot {
width: 10px;
height: 10px;
border-radius: 50%;
}
.legend-item.success .status-dot { background: #67c23a; }
.legend-item.redirect .status-dot { background: #e6a23c; }
.legend-item.client-error .status-dot { background: #f56c6c; }
.legend-item.server-error .status-dot { background: #909399; }
.status-code {
font-family: monospace;
font-size: 12px;
font-weight: 700;
color: var(--vp-c-text-1);
}
.status-meaning {
font-size: 12px;
color: var(--vp-c-text-2);
}
@media (max-width: 640px) {
.scene-background {
flex-direction: column;
}
.character {
order: -1;
}
.legend-grid {
grid-template-columns: 1fr;
}
}
</style>