Files
test-repo/docs/.vitepress/theme/components/appendix/ai-native-app/AIUXPatternDemo.vue
T
sanbuphy 3af119a598 feat(appendix): 添加多个交互式演示组件,完善 AI/Infra 等章节内容
- 新增 Vibe Coding 全栈相关演示组件 (DeveloperSkillShift, FrontendTriad, BackendCore 等)
- 新增 RAG 相关组件 (RAGPipeline, ChunkingStrategy, Retrieval 等)
- 新增 Embedding & Vector 相关组件 (EmbeddingConcept, VectorSimilarity 等)
- 新增 AI Native App 设计组件 (AINativeArch, PromptDesign 等)
- 新增 Infrastructure as Code 组件 (IaCConcept, TerraformWorkflow 等)
- 新增 DNS & HTTPS 演示组件 (DnsResolution, HttpsHandshake 等)
- 新增 Model Finetuning 组件 (FinetuningPipeline 等)
- 更新多个章节的 markdown 内容,集成交互式演示
2026-02-24 18:22:58 +08:00

302 lines
11 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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.
<template>
<div class="ux-demo">
<div class="header">
<div class="title">AI 原生交互模式</div>
<div class="subtitle">点击卡片体验每种 AI 交互模式的效果</div>
</div>
<div class="pattern-grid">
<div
v-for="p in patterns"
:key="p.id"
:class="['pattern-card', { active: activePattern === p.id }]"
@click="activatePattern(p.id)"
>
<div class="card-icon">{{ p.icon }}</div>
<div class="card-name">{{ p.name }}</div>
<div class="card-desc">{{ p.brief }}</div>
</div>
</div>
<div v-if="activePattern" class="preview-area">
<div class="preview-header">
<span>{{ currentPattern.icon }} {{ currentPattern.name }} 演示</span>
<button class="replay-btn" @click="replayDemo">🔄 重播</button>
</div>
<!-- 流式输出演示 -->
<div v-if="activePattern === 'streaming'" class="demo-box">
<div class="chat-bubble ai">
<span class="stream-text">{{ streamText }}</span>
<span v-if="isStreaming" class="cursor-blink">|</span>
</div>
<div class="demo-note">逐字输出用户无需等待完整响应</div>
</div>
<!-- 加载状态演示 -->
<div v-if="activePattern === 'loading'" class="demo-box">
<div class="loading-stages">
<div
v-for="(s, idx) in loadingStages"
:key="idx"
:class="['stage', { done: loadingStep > idx, current: loadingStep === idx }]"
>
<span class="stage-icon">
{{ loadingStep > idx ? '✅' : loadingStep === idx ? '⏳' : '⬜' }}
</span>
<span>{{ s }}</span>
</div>
</div>
<div class="demo-note">分阶段展示进度而非单一的"加载中"</div>
</div>
<!-- 置信度指示器演示 -->
<div v-if="activePattern === 'confidence'" class="demo-box">
<div class="confidence-list">
<div v-for="c in confidenceItems" :key="c.text" class="conf-item">
<div class="conf-bar-wrap">
<div
class="conf-bar"
:style="{ width: c.score + '%', background: c.color }"
/>
</div>
<div class="conf-score">{{ c.score }}%</div>
<div class="conf-label">{{ c.level }}</div>
<div class="conf-text">{{ c.text }}</div>
</div>
</div>
<div class="demo-note">让用户知道 AI 对自己的回答有多"确定"</div>
</div>
<!-- 降级处理演示 -->
<div v-if="activePattern === 'fallback'" class="demo-box">
<div class="fallback-flow">
<div :class="['fb-step', { active: fallbackStep >= 0 }]">
<span class="fb-icon">🤖</span>
<span>AI 尝试回答...</span>
</div>
<div class="fb-arrow" v-if="fallbackStep >= 1"> 检测到不确定</div>
<div :class="['fb-step warn', { active: fallbackStep >= 1 }]">
<span class="fb-icon"></span>
<span>提示用户此回答可能不准确</span>
</div>
<div class="fb-arrow" v-if="fallbackStep >= 2"> 提供替代方案</div>
<div :class="['fb-step safe', { active: fallbackStep >= 2 }]">
<span class="fb-icon">🔄</span>
<span>转接人工 / 推荐文档 / 换个方式提问</span>
</div>
</div>
<div class="demo-note">AI 不确定时优雅降级而非强行回答</div>
</div>
<div class="pattern-detail">
<div class="detail-label">设计要点</div>
<div class="detail-text">{{ currentPattern.detail }}</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const patterns = [
{
id: 'streaming', icon: '💬', name: '流式输出',
brief: '逐字生成,即时反馈',
detail: '流式输出让用户在 AI 思考时就能看到部分结果,大幅降低感知等待时间。技术上通过 SSEServer-Sent Events)或 WebSocket 实现,前端逐步渲染 Markdown 内容。'
},
{
id: 'loading', icon: '⏳', name: '智能加载态',
brief: '分阶段展示处理进度',
detail: 'AI 请求通常需要数秒,传统的转圈加载会让用户焦虑。智能加载态将处理过程拆解为可见的步骤(理解问题 → 检索知识 → 生成回答),让等待变得可预期。'
},
{
id: 'confidence', icon: '📊', name: '置信度指示',
brief: '展示 AI 的确定程度',
detail: 'AI 的输出具有概率性,不同回答的可靠程度不同。通过置信度指示器,用户可以判断哪些信息可以直接采纳,哪些需要二次验证。这是 AI 原生应用透明性的核心体现。'
},
{
id: 'fallback', icon: '🛡️', name: '优雅降级',
brief: '不确定时的兜底策略',
detail: '当 AI 无法给出可靠回答时,不应该硬编一个答案。优雅降级策略包括:坦诚告知不确定性、提供替代信息源、转接人工服务、引导用户换个方式提问。'
}
]
const activePattern = ref('')
const currentPattern = computed(() => patterns.find(p => p.id === activePattern.value) || {})
// Streaming demo
const streamText = ref('')
const isStreaming = ref(false)
const fullText = 'React 是一个用于构建用户界面的 JavaScript 库。它采用组件化的开发模式,让你可以将复杂的 UI 拆分成独立的、可复用的小模块。'
// Loading demo
const loadingStages = ['理解用户意图...', '检索相关知识...', '组织回答内容...', '生成最终响应']
const loadingStep = ref(-1)
// Confidence demo
const confidenceItems = [
{ text: 'React 由 Meta 开发', score: 98, level: '高置信', color: '#10b981' },
{ text: '全球约 40% 的网站使用 React', score: 72, level: '中置信', color: '#f59e0b' },
{ text: 'React 19 将在下月发布', score: 35, level: '低置信', color: '#ef4444' }
]
// Fallback demo
const fallbackStep = ref(-1)
let timer = null
const clearTimers = () => {
if (timer) { clearInterval(timer); timer = null }
}
const activatePattern = (id) => {
clearTimers()
activePattern.value = id
replayDemo()
}
const replayDemo = () => {
clearTimers()
if (activePattern.value === 'streaming') {
streamText.value = ''
isStreaming.value = true
let i = 0
timer = setInterval(() => {
if (i < fullText.length) {
streamText.value += fullText[i]
i++
} else {
isStreaming.value = false
clearTimers()
}
}, 50)
} else if (activePattern.value === 'loading') {
loadingStep.value = 0
let step = 0
timer = setInterval(() => {
step++
loadingStep.value = step
if (step >= loadingStages.length) clearTimers()
}, 900)
} else if (activePattern.value === 'fallback') {
fallbackStep.value = 0
let step = 0
timer = setInterval(() => {
step++
fallbackStep.value = step
if (step >= 2) clearTimers()
}, 1000)
}
}
</script>
<style scoped>
.ux-demo {
background: var(--vp-c-bg-soft);
border: 1px solid var(--vp-c-divider);
border-radius: 12px; padding: 20px; margin: 20px 0;
}
.header { text-align: center; margin-bottom: 16px; }
.title {
font-size: 17px; font-weight: 700;
background: linear-gradient(120deg, #06b6d4, #8b5cf6);
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
}
.subtitle { font-size: 12px; color: var(--vp-c-text-2); margin-top: 4px; }
.pattern-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
gap: 10px; margin-bottom: 16px;
}
.pattern-card {
background: var(--vp-c-bg); border: 1px solid var(--vp-c-divider);
border-radius: 10px; padding: 14px; cursor: pointer;
transition: all 0.2s; text-align: center;
}
.pattern-card:hover { background: var(--vp-c-bg-alt); }
.pattern-card.active {
border-color: var(--vp-c-brand);
box-shadow: 0 4px 12px rgba(0,0,0,0.06);
}
.card-icon { font-size: 24px; margin-bottom: 6px; }
.card-name { font-weight: 600; font-size: 13px; }
.card-desc { font-size: 11px; color: var(--vp-c-text-2); margin-top: 4px; }
.preview-area {
background: var(--vp-c-bg); border: 1px solid var(--vp-c-divider);
border-radius: 12px; padding: 16px;
}
.preview-header {
display: flex; justify-content: space-between; align-items: center;
font-weight: 700; font-size: 14px; margin-bottom: 12px;
}
.replay-btn {
padding: 4px 12px; border: 1px solid var(--vp-c-divider);
border-radius: 6px; background: var(--vp-c-bg-soft);
cursor: pointer; font-size: 12px;
}
.demo-box {
background: var(--vp-c-bg-soft); border-radius: 8px;
padding: 16px; margin-bottom: 12px;
}
.demo-note {
font-size: 11px; color: var(--vp-c-text-3);
text-align: center; margin-top: 10px;
}
/* Streaming */
.chat-bubble.ai {
background: var(--vp-c-bg); border: 1px solid var(--vp-c-divider);
border-radius: 10px; padding: 12px; font-size: 13px; line-height: 1.7;
}
.cursor-blink { animation: blink 0.8s infinite; color: var(--vp-c-brand); }
@keyframes blink { 50% { opacity: 0; } }
/* Loading */
.loading-stages { display: flex; flex-direction: column; gap: 8px; }
.stage {
display: flex; align-items: center; gap: 8px;
padding: 8px 12px; border-radius: 6px; font-size: 13px;
background: var(--vp-c-bg); border: 1px solid var(--vp-c-divider);
opacity: 0.4; transition: all 0.3s;
}
.stage.current { opacity: 1; border-color: var(--vp-c-brand); background: var(--vp-c-brand-soft); }
.stage.done { opacity: 1; border-color: #86efac; background: #f0fdf4; }
/* Confidence */
.confidence-list { display: flex; flex-direction: column; gap: 10px; }
.conf-item {
display: grid; grid-template-columns: 1fr 40px 60px 1fr;
align-items: center; gap: 8px; font-size: 12px;
}
.conf-bar-wrap {
height: 8px; background: var(--vp-c-bg);
border-radius: 4px; overflow: hidden;
}
.conf-bar { height: 100%; border-radius: 4px; transition: width 0.6s; }
.conf-score { font-weight: 600; text-align: right; }
.conf-label { font-size: 11px; color: var(--vp-c-text-2); }
.conf-text { color: var(--vp-c-text-1); }
/* Fallback */
.fallback-flow { display: flex; flex-direction: column; align-items: center; gap: 6px; }
.fb-step {
display: flex; align-items: center; gap: 8px; width: 100%;
padding: 10px 14px; border-radius: 8px; font-size: 13px;
background: var(--vp-c-bg); border: 1px solid var(--vp-c-divider);
opacity: 0.3; transition: all 0.4s;
}
.fb-step.active { opacity: 1; }
.fb-step.warn.active { border-color: #fbbf24; background: #fef3c7; }
.fb-step.safe.active { border-color: #86efac; background: #f0fdf4; }
.fb-arrow { font-size: 12px; color: var(--vp-c-text-3); }
.pattern-detail { margin-top: 12px; }
.detail-label { font-weight: 600; font-size: 12px; margin-bottom: 4px; }
.detail-text { font-size: 13px; color: var(--vp-c-text-2); line-height: 1.7; }
</style>